A hackable template for creating small and fast browser games.

Add RenderMapped

+427 -45
+4
Shading/game.ts
··· 3 import {mat1_colored_phong} from "../materials/mat1_colored_phong.js"; 4 import {mat1_colored_points} from "../materials/mat1_colored_points.js"; 5 import {mat1_colored_unlit, mat1_colored_wireframe} from "../materials/mat1_colored_unlit.js"; 6 import {mat1_textured_gouraud} from "../materials/mat1_textured_gouraud.js"; 7 import {mat1_textured_phong} from "../materials/mat1_textured_phong.js"; 8 import {mat1_textured_unlit} from "../materials/mat1_textured_unlit.js"; 9 import {mesh_icosphere_smooth} from "../meshes/icosphere_smooth.js"; 10 import {Camera} from "./components/com_camera.js"; 11 import {loop_start, loop_stop} from "./impl.js"; ··· 40 MaterialTexturedUnlit = mat1_textured_unlit(this.Gl); 41 MaterialTexturedGouraud = mat1_textured_gouraud(this.Gl); 42 MaterialTexturedPhong = mat1_textured_phong(this.Gl); 43 44 MeshIcosphereSmooth = mesh_icosphere_smooth(this.Gl); 45 46 Textures: Record<string, WebGLTexture> = {};
··· 3 import {mat1_colored_phong} from "../materials/mat1_colored_phong.js"; 4 import {mat1_colored_points} from "../materials/mat1_colored_points.js"; 5 import {mat1_colored_unlit, mat1_colored_wireframe} from "../materials/mat1_colored_unlit.js"; 6 + import {mat1_mapped} from "../materials/mat1_mapped.js"; 7 import {mat1_textured_gouraud} from "../materials/mat1_textured_gouraud.js"; 8 import {mat1_textured_phong} from "../materials/mat1_textured_phong.js"; 9 import {mat1_textured_unlit} from "../materials/mat1_textured_unlit.js"; 10 + import {mesh_cube} from "../meshes/cube.js"; 11 import {mesh_icosphere_smooth} from "../meshes/icosphere_smooth.js"; 12 import {Camera} from "./components/com_camera.js"; 13 import {loop_start, loop_stop} from "./impl.js"; ··· 42 MaterialTexturedUnlit = mat1_textured_unlit(this.Gl); 43 MaterialTexturedGouraud = mat1_textured_gouraud(this.Gl); 44 MaterialTexturedPhong = mat1_textured_phong(this.Gl); 45 + MaterialMapped = mat1_mapped(this.Gl); 46 47 + MeshCube = mesh_cube(this.Gl); 48 MeshIcosphereSmooth = mesh_icosphere_smooth(this.Gl); 49 50 Textures: Record<string, WebGLTexture> = {};
+13 -2
Shading/index.ts
··· 8 // @ts-ignore 9 window.game = game; 10 11 - Promise.all([load_texture(game, "checker1.webp")]).then(() => { 12 scene_stage(game); 13 loop_start(game); 14 }); 15 16 async function load_texture(game: Game, name: string) { 17 - let image = await fetch_image("../textures/" + name); 18 game.Textures[name] = create_texture_from(game.Gl, image); 19 }
··· 8 // @ts-ignore 9 window.game = game; 10 11 + Promise.all([ 12 + load_texture(game, "checker1.png"), 13 + load_texture(game, "Bricks059_1K_Color.jpg"), 14 + load_texture(game, "Bricks059_1K_Normal.jpg"), 15 + load_texture(game, "Bricks059_1K_Roughness.jpg"), 16 + load_texture(game, "Sponge001_1K_Color.jpg"), 17 + load_texture(game, "Sponge001_1K_Normal.jpg"), 18 + load_texture(game, "Sponge001_1K_Roughness.jpg"), 19 + ]).then(() => { 20 scene_stage(game); 21 loop_start(game); 22 }); 23 24 async function load_texture(game: Game, name: string) { 25 + let image = await fetch_image("../textures/" + name + ".webp"); 26 game.Textures[name] = create_texture_from(game.Gl, image); 27 + 28 + // Report loading progress. 29 + game.Ui.innerHTML += `Loading <code>${name}</code>... ✓<br>`; 30 }
+53 -40
Shading/scenes/sce_stage.ts
··· 6 import { 7 render_colored_shaded, 8 render_colored_unlit, 9 render_textured_shaded, 10 render_textured_unlit, 11 } from "../components/com_render1.js"; ··· 44 ]); 45 46 let shadings = [ 47 - // Unlit column 48 render_colored_unlit(game.MaterialColoredPoints, game.MeshIcosphereSmooth, [1, 1, 0, 1]), 49 render_colored_unlit(game.MaterialColoredWireframe, game.MeshIcosphereSmooth, [1, 1, 0, 1]), 50 render_colored_unlit(game.MaterialColoredUnlit, game.MeshIcosphereSmooth, [1, 1, 0, 1]), 51 - render_textured_unlit( 52 - game.MaterialTexturedUnlit, 53 - game.MeshIcosphereSmooth, 54 - game.Textures["checker1.webp"] 55 - ), 56 57 // Colored Gouraud shading 58 render_colored_shaded(game.MaterialColoredGouraud, game.MeshIcosphereSmooth, [1, 1, 0, 1]), ··· 102 [1, 1, 0, 1] 103 ), 104 105 - // Textured Gouraud shading 106 - render_textured_shaded( 107 - game.MaterialTexturedGouraud, 108 - game.MeshIcosphereSmooth, 109 - game.Textures["checker1.webp"] 110 ), 111 - render_textured_shaded( 112 - game.MaterialTexturedGouraud, 113 - game.MeshIcosphereSmooth, 114 - game.Textures["checker1.webp"], 115 - 16 116 ), 117 render_textured_shaded( 118 game.MaterialTexturedGouraud, 119 - game.MeshIcosphereSmooth, 120 - game.Textures["checker1.webp"], 121 - 128 122 ), 123 render_textured_shaded( 124 game.MaterialTexturedGouraud, 125 - game.MeshIcosphereSmooth, 126 - game.Textures["checker1.webp"], 127 - 512 128 ), 129 130 - // Textured Phong shading 131 render_textured_shaded( 132 game.MaterialTexturedPhong, 133 - game.MeshIcosphereSmooth, 134 - game.Textures["checker1.webp"] 135 ), 136 render_textured_shaded( 137 game.MaterialTexturedPhong, 138 - game.MeshIcosphereSmooth, 139 - game.Textures["checker1.webp"], 140 - 16 141 ), 142 - render_textured_shaded( 143 - game.MaterialTexturedPhong, 144 - game.MeshIcosphereSmooth, 145 - game.Textures["checker1.webp"], 146 - 128 147 ), 148 - render_textured_shaded( 149 - game.MaterialTexturedPhong, 150 - game.MeshIcosphereSmooth, 151 - game.Textures["checker1.webp"], 152 - 512 153 ), 154 ]; 155 156 let rows = 4; 157 - let cols = 5; 158 let pad = 0.25; 159 160 let offset_x = (cols + pad * (cols - 1)) / 2; ··· 166 let render = shadings.shift(); 167 if (render) { 168 let x = col * (1 + pad) + 0.5; 169 - instantiate(game, [transform([x - offset_x, y - offset_y, 0]), render]); 170 } 171 } 172 }
··· 6 import { 7 render_colored_shaded, 8 render_colored_unlit, 9 + render_mapped, 10 render_textured_shaded, 11 render_textured_unlit, 12 } from "../components/com_render1.js"; ··· 45 ]); 46 47 let shadings = [ 48 + // Unlit shading 49 render_colored_unlit(game.MaterialColoredPoints, game.MeshIcosphereSmooth, [1, 1, 0, 1]), 50 render_colored_unlit(game.MaterialColoredWireframe, game.MeshIcosphereSmooth, [1, 1, 0, 1]), 51 render_colored_unlit(game.MaterialColoredUnlit, game.MeshIcosphereSmooth, [1, 1, 0, 1]), 52 + undefined, 53 54 // Colored Gouraud shading 55 render_colored_shaded(game.MaterialColoredGouraud, game.MeshIcosphereSmooth, [1, 1, 0, 1]), ··· 99 [1, 1, 0, 1] 100 ), 101 102 + // Textured unlit shading 103 + render_textured_unlit( 104 + game.MaterialTexturedUnlit, 105 + game.MeshCube, 106 + game.Textures["Bricks059_1K_Color.jpg"] 107 ), 108 + render_textured_unlit( 109 + game.MaterialTexturedUnlit, 110 + game.MeshCube, 111 + game.Textures["Sponge001_1K_Color.jpg"] 112 ), 113 + undefined, 114 + undefined, 115 + 116 + // Textured Gouraud shading (diffuse only) 117 render_textured_shaded( 118 game.MaterialTexturedGouraud, 119 + game.MeshCube, 120 + game.Textures["Bricks059_1K_Color.jpg"] 121 ), 122 render_textured_shaded( 123 game.MaterialTexturedGouraud, 124 + game.MeshCube, 125 + game.Textures["Sponge001_1K_Color.jpg"] 126 ), 127 + undefined, 128 + undefined, 129 130 + // Textured Phong shading (high specular) 131 render_textured_shaded( 132 game.MaterialTexturedPhong, 133 + game.MeshCube, 134 + game.Textures["Bricks059_1K_Color.jpg"], 135 + 512 136 ), 137 render_textured_shaded( 138 game.MaterialTexturedPhong, 139 + game.MeshCube, 140 + game.Textures["Sponge001_1K_Color.jpg"], 141 + 512 142 ), 143 + undefined, 144 + undefined, 145 + 146 + // Mapped (diffuse, normal, roughness) shading 147 + render_mapped( 148 + game.MaterialMapped, 149 + game.MeshCube, 150 + game.Textures["Bricks059_1K_Color.jpg"], 151 + game.Textures["Bricks059_1K_Normal.jpg"], 152 + game.Textures["Bricks059_1K_Roughness.jpg"] 153 ), 154 + 155 + render_mapped( 156 + game.MaterialMapped, 157 + game.MeshCube, 158 + game.Textures["Sponge001_1K_Color.jpg"], 159 + game.Textures["Sponge001_1K_Normal.jpg"], 160 + game.Textures["Sponge001_1K_Roughness.jpg"] 161 ), 162 ]; 163 164 let rows = 4; 165 + let cols = 7; 166 let pad = 0.25; 167 168 let offset_x = (cols + pad * (cols - 1)) / 2; ··· 174 let render = shadings.shift(); 175 if (render) { 176 let x = col * (1 + pad) + 0.5; 177 + instantiate(game, [ 178 + transform([x - offset_x, y - offset_y, 0]), 179 + control_always(null, [0, 1, 0, 0]), 180 + move(0, 0.1), 181 + render, 182 + ]); 183 } 184 } 185 }
+155 -1
core/components/com_render1.ts
··· 1 import {Material, Mesh} from "../../common/material.js"; 2 - import {Vec4} from "../../common/math.js"; 3 import { 4 GL_ARRAY_BUFFER, 5 GL_CW, 6 GL_DYNAMIC_DRAW, 7 GL_ELEMENT_ARRAY_BUFFER, 8 GL_FLOAT, 9 } from "../../common/webgl.js"; 10 import {ColoredShadedLayout} from "../../materials/layout_colored_shaded.js"; 11 import {ColoredUnlitLayout} from "../../materials/layout_colored_unlit.js"; 12 import {TexturedShadedLayout} from "../../materials/layout_textured_shaded.js"; 13 import {TexturedUnlitLayout} from "../../materials/layout_textured_unlit.js"; 14 import {Entity, Game} from "../game.js"; ··· 19 | RenderColoredShaded 20 | RenderTexturedUnlit 21 | RenderTexturedShaded 22 | RenderVertices; 23 24 export const enum RenderKind { ··· 26 ColoredShaded, 27 TexturedUnlit, 28 TexturedShaded, 29 Vertices, 30 } 31 ··· 317 }; 318 }; 319 }
··· 1 import {Material, Mesh} from "../../common/material.js"; 2 + import {Vec3, Vec4} from "../../common/math.js"; 3 + import {normalize, subtract} from "../../common/vec3.js"; 4 import { 5 GL_ARRAY_BUFFER, 6 GL_CW, 7 GL_DYNAMIC_DRAW, 8 GL_ELEMENT_ARRAY_BUFFER, 9 GL_FLOAT, 10 + GL_STATIC_DRAW, 11 } from "../../common/webgl.js"; 12 import {ColoredShadedLayout} from "../../materials/layout_colored_shaded.js"; 13 import {ColoredUnlitLayout} from "../../materials/layout_colored_unlit.js"; 14 + import {MappedLayout} from "../../materials/layout_mapped.js"; 15 import {TexturedShadedLayout} from "../../materials/layout_textured_shaded.js"; 16 import {TexturedUnlitLayout} from "../../materials/layout_textured_unlit.js"; 17 import {Entity, Game} from "../game.js"; ··· 22 | RenderColoredShaded 23 | RenderTexturedUnlit 24 | RenderTexturedShaded 25 + | RenderMapped 26 | RenderVertices; 27 28 export const enum RenderKind { ··· 30 ColoredShaded, 31 TexturedUnlit, 32 TexturedShaded, 33 + Mapped, 34 Vertices, 35 } 36 ··· 322 }; 323 }; 324 } 325 + 326 + export interface RenderMapped { 327 + readonly Kind: RenderKind.Mapped; 328 + readonly Material: Material<MappedLayout>; 329 + readonly Mesh: Mesh; 330 + readonly FrontFace: GLenum; 331 + readonly Vao: WebGLVertexArrayObject; 332 + DiffuseMap: WebGLTexture; 333 + DiffuseColor: Vec4; 334 + NormalMap: WebGLTexture; 335 + RoughnessMap: WebGLTexture; 336 + } 337 + 338 + let mapped_vaos: WeakMap<Mesh, WebGLVertexArrayObject> = new WeakMap(); 339 + 340 + export function render_mapped( 341 + material: Material<MappedLayout>, 342 + mesh: Mesh, 343 + diffuse_map: WebGLTexture, 344 + normal_map: WebGLTexture, 345 + roughness_map: WebGLTexture, 346 + diffuse_color: Vec4 = [1, 1, 1, 1] 347 + ) { 348 + return (game: Game1, entity: Entity) => { 349 + if (!mapped_vaos.has(mesh)) { 350 + // We only need to create the VAO once. 351 + let vao = game.ExtVao.createVertexArrayOES()!; 352 + game.ExtVao.bindVertexArrayOES(vao); 353 + 354 + game.Gl.bindBuffer(GL_ARRAY_BUFFER, mesh.VertexBuffer); 355 + game.Gl.enableVertexAttribArray(material.Locations.VertexPosition); 356 + game.Gl.vertexAttribPointer( 357 + material.Locations.VertexPosition, 358 + 3, 359 + GL_FLOAT, 360 + false, 361 + 0, 362 + 0 363 + ); 364 + 365 + game.Gl.bindBuffer(GL_ARRAY_BUFFER, mesh.NormalBuffer); 366 + game.Gl.enableVertexAttribArray(material.Locations.VertexNormal); 367 + game.Gl.vertexAttribPointer(material.Locations.VertexNormal, 3, GL_FLOAT, false, 0, 0); 368 + 369 + game.Gl.bindBuffer(GL_ARRAY_BUFFER, mesh.TexCoordBuffer); 370 + game.Gl.enableVertexAttribArray(material.Locations.VertexTexCoord); 371 + game.Gl.vertexAttribPointer( 372 + material.Locations.VertexTexCoord, 373 + 2, 374 + GL_FLOAT, 375 + false, 376 + 0, 377 + 0 378 + ); 379 + 380 + game.Gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh.IndexBuffer); 381 + 382 + let tangent_arr = new Float32Array(mesh.NormalArray.length); 383 + let bitangent_arr = new Float32Array(mesh.NormalArray.length); 384 + 385 + for (let i = 0; i < mesh.IndexCount; i += 3) { 386 + let v0 = mesh.IndexArray[i + 0]; 387 + let v1 = mesh.IndexArray[i + 1]; 388 + let v2 = mesh.IndexArray[i + 2]; 389 + 390 + let p0: Vec3 = [ 391 + mesh.VertexArray[v0 * 3 + 0], 392 + mesh.VertexArray[v0 * 3 + 1], 393 + mesh.VertexArray[v0 * 3 + 2], 394 + ]; 395 + let p1: Vec3 = [ 396 + mesh.VertexArray[v1 * 3 + 0], 397 + mesh.VertexArray[v1 * 3 + 1], 398 + mesh.VertexArray[v1 * 3 + 2], 399 + ]; 400 + let p2: Vec3 = [ 401 + mesh.VertexArray[v2 * 3 + 0], 402 + mesh.VertexArray[v2 * 3 + 1], 403 + mesh.VertexArray[v2 * 3 + 2], 404 + ]; 405 + 406 + let edge1 = subtract([0, 0, 0], p1, p0); 407 + let edge2 = subtract([0, 0, 0], p2, p0); 408 + 409 + let delta_u1 = mesh.TexCoordArray[v1 * 2 + 0] - mesh.TexCoordArray[v0 * 2 + 0]; 410 + let delta_v1 = mesh.TexCoordArray[v1 * 2 + 1] - mesh.TexCoordArray[v0 * 2 + 1]; 411 + let delta_u2 = mesh.TexCoordArray[v2 * 2 + 0] - mesh.TexCoordArray[v0 * 2 + 0]; 412 + let delta_v2 = mesh.TexCoordArray[v2 * 2 + 1] - mesh.TexCoordArray[v0 * 2 + 1]; 413 + 414 + let r = 1 / (delta_u1 * delta_v2 - delta_u2 * delta_v1); 415 + let tangent: Vec3 = [ 416 + r * (delta_v2 * edge1[0] - delta_v1 * edge2[0]), 417 + r * (delta_v2 * edge1[1] - delta_v1 * edge2[1]), 418 + r * (delta_v2 * edge1[2] - delta_v1 * edge2[2]), 419 + ]; 420 + let bitangent: Vec3 = [ 421 + r * (-delta_u2 * edge1[0] + delta_u1 * edge2[0]), 422 + r * (-delta_u2 * edge1[1] + delta_u1 * edge2[1]), 423 + r * (-delta_u2 * edge1[2] + delta_u1 * edge2[2]), 424 + ]; 425 + 426 + normalize(tangent, tangent); 427 + tangent_arr.set(tangent, v0 * 2); 428 + tangent_arr.set(tangent, v1 * 2); 429 + tangent_arr.set(tangent, v2 * 2); 430 + 431 + normalize(bitangent, bitangent); 432 + bitangent_arr.set(bitangent, v0 * 2); 433 + bitangent_arr.set(bitangent, v1 * 2); 434 + bitangent_arr.set(bitangent, v2 * 2); 435 + } 436 + 437 + let tangent_buf = game.Gl.createBuffer()!; 438 + game.Gl.bindBuffer(GL_ARRAY_BUFFER, tangent_buf); 439 + game.Gl.bufferData(GL_ARRAY_BUFFER, tangent_arr, GL_STATIC_DRAW); 440 + game.Gl.enableVertexAttribArray(material.Locations.VertexTangent); 441 + game.Gl.vertexAttribPointer(material.Locations.VertexTangent, 3, GL_FLOAT, false, 0, 0); 442 + 443 + let bitangent_buf = game.Gl.createBuffer()!; 444 + game.Gl.bindBuffer(GL_ARRAY_BUFFER, bitangent_buf); 445 + game.Gl.bufferData(GL_ARRAY_BUFFER, bitangent_arr, GL_STATIC_DRAW); 446 + game.Gl.enableVertexAttribArray(material.Locations.VertexBitangent); 447 + game.Gl.vertexAttribPointer( 448 + material.Locations.VertexBitangent, 449 + 3, 450 + GL_FLOAT, 451 + false, 452 + 0, 453 + 0 454 + ); 455 + 456 + game.ExtVao.bindVertexArrayOES(null); 457 + mapped_vaos.set(mesh, vao); 458 + } 459 + 460 + game.World.Signature[entity] |= Has.Render; 461 + game.World.Render[entity] = { 462 + Kind: RenderKind.Mapped, 463 + Material: material, 464 + Mesh: mesh, 465 + FrontFace: GL_CW, 466 + Vao: mapped_vaos.get(mesh)!, 467 + DiffuseMap: diffuse_map, 468 + DiffuseColor: diffuse_color, 469 + NormalMap: normal_map, 470 + RoughnessMap: roughness_map, 471 + }; 472 + }; 473 + }
+42
core/systems/sys_render1.ts
··· 6 GL_FLOAT, 7 GL_FRAMEBUFFER, 8 GL_TEXTURE0, 9 GL_TEXTURE_2D, 10 GL_UNSIGNED_SHORT, 11 } from "../../common/webgl.js"; 12 import {ColoredShadedLayout} from "../../materials/layout_colored_shaded.js"; 13 import {ColoredUnlitLayout} from "../../materials/layout_colored_unlit.js"; 14 import {TexturedShadedLayout} from "../../materials/layout_textured_shaded.js"; 15 import {TexturedUnlitLayout} from "../../materials/layout_textured_unlit.js"; 16 import {CameraDisplay, CameraEye, CameraFramebuffer, CameraKind} from "../components/com_camera.js"; ··· 19 RenderColoredShaded, 20 RenderColoredUnlit, 21 RenderKind, 22 RenderTexturedShaded, 23 RenderTexturedUnlit, 24 RenderVertices, ··· 94 case RenderKind.Vertices: 95 use_vertices(game, render.Material, eye); 96 break; 97 } 98 } 99 ··· 125 break; 126 case RenderKind.Vertices: 127 draw_vertices(game, transform, render); 128 break; 129 } 130 } ··· 223 game.Gl.vertexAttribPointer(render.Material.Locations.VertexPosition, 3, GL_FLOAT, false, 0, 0); 224 game.Gl.drawArrays(render.Material.Mode, 0, render.IndexCount); 225 }
··· 6 GL_FLOAT, 7 GL_FRAMEBUFFER, 8 GL_TEXTURE0, 9 + GL_TEXTURE1, 10 + GL_TEXTURE2, 11 + GL_TEXTURE3, 12 GL_TEXTURE_2D, 13 GL_UNSIGNED_SHORT, 14 } from "../../common/webgl.js"; 15 import {ColoredShadedLayout} from "../../materials/layout_colored_shaded.js"; 16 import {ColoredUnlitLayout} from "../../materials/layout_colored_unlit.js"; 17 + import {MappedLayout} from "../../materials/layout_mapped.js"; 18 import {TexturedShadedLayout} from "../../materials/layout_textured_shaded.js"; 19 import {TexturedUnlitLayout} from "../../materials/layout_textured_unlit.js"; 20 import {CameraDisplay, CameraEye, CameraFramebuffer, CameraKind} from "../components/com_camera.js"; ··· 23 RenderColoredShaded, 24 RenderColoredUnlit, 25 RenderKind, 26 + RenderMapped, 27 RenderTexturedShaded, 28 RenderTexturedUnlit, 29 RenderVertices, ··· 99 case RenderKind.Vertices: 100 use_vertices(game, render.Material, eye); 101 break; 102 + case RenderKind.Mapped: 103 + use_mapped(game, render.Material, eye); 104 + break; 105 } 106 } 107 ··· 133 break; 134 case RenderKind.Vertices: 135 draw_vertices(game, transform, render); 136 + break; 137 + case RenderKind.Mapped: 138 + draw_mapped(game, transform, render); 139 break; 140 } 141 } ··· 234 game.Gl.vertexAttribPointer(render.Material.Locations.VertexPosition, 3, GL_FLOAT, false, 0, 0); 235 game.Gl.drawArrays(render.Material.Mode, 0, render.IndexCount); 236 } 237 + 238 + function use_mapped(game: Game1, material: Material<MappedLayout>, eye: CameraEye) { 239 + game.Gl.useProgram(material.Program); 240 + game.Gl.uniformMatrix4fv(material.Locations.Pv, false, eye.Pv); 241 + game.Gl.uniform3fv(material.Locations.Eye, eye.Position); 242 + game.Gl.uniform4fv(material.Locations.LightPositions, game.LightPositions); 243 + game.Gl.uniform4fv(material.Locations.LightDetails, game.LightDetails); 244 + } 245 + 246 + function draw_mapped(game: Game1, transform: Transform, render: RenderMapped) { 247 + game.Gl.uniformMatrix4fv(render.Material.Locations.World, false, transform.World); 248 + game.Gl.uniformMatrix4fv(render.Material.Locations.Self, false, transform.Self); 249 + 250 + game.Gl.uniform4fv(render.Material.Locations.DiffuseColor, render.DiffuseColor); 251 + 252 + game.Gl.activeTexture(GL_TEXTURE1); 253 + game.Gl.bindTexture(GL_TEXTURE_2D, render.DiffuseMap); 254 + game.Gl.uniform1i(render.Material.Locations.DiffuseMap, 1); 255 + 256 + game.Gl.activeTexture(GL_TEXTURE2); 257 + game.Gl.bindTexture(GL_TEXTURE_2D, render.NormalMap); 258 + game.Gl.uniform1i(render.Material.Locations.NormalMap, 2); 259 + 260 + game.Gl.activeTexture(GL_TEXTURE3); 261 + game.Gl.bindTexture(GL_TEXTURE_2D, render.RoughnessMap); 262 + game.Gl.uniform1i(render.Material.Locations.RoughnessMap, 3); 263 + 264 + game.ExtVao.bindVertexArrayOES(render.Vao); 265 + game.Gl.drawElements(render.Material.Mode, render.Mesh.IndexCount, GL_UNSIGNED_SHORT, 0); 266 + game.ExtVao.bindVertexArrayOES(null); 267 + }
+19
materials/layout_mapped.ts
···
··· 1 + export interface MappedLayout { 2 + // Uniforms 3 + Pv: WebGLUniformLocation; 4 + World: WebGLUniformLocation; 5 + Self: WebGLUniformLocation; 6 + Eye: WebGLUniformLocation; 7 + DiffuseMap: WebGLUniformLocation; 8 + DiffuseColor: WebGLUniformLocation; 9 + NormalMap: WebGLUniformLocation; 10 + RoughnessMap: WebGLUniformLocation; 11 + LightPositions: WebGLUniformLocation; 12 + LightDetails: WebGLUniformLocation; 13 + // Attributes 14 + VertexPosition: GLint; 15 + VertexTexCoord: GLint; 16 + VertexNormal: GLint; 17 + VertexTangent: GLint; 18 + VertexBitangent: GLint; 19 + }
+134
materials/mat1_mapped.ts
···
··· 1 + import {link, Material} from "../common/material.js"; 2 + import {GL_TRIANGLES} from "../common/webgl.js"; 3 + import {MappedLayout} from "./layout_mapped.js"; 4 + 5 + let vertex = ` 6 + uniform mat4 pv; 7 + uniform mat4 world; 8 + 9 + attribute vec3 attr_position; 10 + attribute vec2 attr_texcoord; 11 + attribute vec3 attr_normal; 12 + attribute vec3 attr_tangent; 13 + attribute vec3 attr_bitangent; 14 + 15 + varying vec4 vert_position; 16 + varying vec2 vert_texcoord; 17 + varying vec3 vert_normal; 18 + varying mat3 vert_tbn; 19 + 20 + void main() { 21 + vert_position = world * vec4(attr_position, 1.0); 22 + gl_Position = pv * vert_position; 23 + 24 + vert_texcoord = attr_texcoord; 25 + vert_tbn = mat3(attr_tangent, attr_bitangent, attr_normal); 26 + } 27 + `; 28 + 29 + let fragment = ` 30 + precision mediump float; 31 + 32 + // See Game.LightPositions and Game.LightDetails. 33 + const int MAX_LIGHTS = 8; 34 + 35 + uniform mat4 self; 36 + 37 + uniform vec4 diffuse_color; 38 + uniform sampler2D diffuse_map; 39 + uniform sampler2D normal_map; 40 + uniform sampler2D roughness_map; 41 + 42 + uniform vec3 eye; 43 + uniform vec4 light_positions[MAX_LIGHTS]; 44 + uniform vec4 light_details[MAX_LIGHTS]; 45 + 46 + varying vec4 vert_position; 47 + varying vec2 vert_texcoord; 48 + varying vec3 vert_normal; 49 + varying mat3 vert_tbn; 50 + 51 + void main() { 52 + vec3 tex_normal = texture2D(normal_map, vert_texcoord).rgb; 53 + vec3 frag_normal = vert_tbn * normalize(tex_normal * 2.0 - 1.0); 54 + vec3 world_normal = (vec4(frag_normal, 1.0) * self).xyz; 55 + 56 + vec3 view_dir = eye - vert_position.xyz; 57 + vec3 view_normal = normalize(view_dir); 58 + 59 + vec4 tex_color = texture2D(diffuse_map, vert_texcoord); 60 + vec3 unlit_rgb = tex_color.rgb * diffuse_color.rgb; 61 + 62 + // Ambient light. 63 + vec3 light_acc = unlit_rgb * 0.1; 64 + 65 + for (int i = 0; i < MAX_LIGHTS; i++) { 66 + if (light_positions[i].w == 0.0) { 67 + break; 68 + } 69 + 70 + vec3 light_color = light_details[i].rgb; 71 + float light_intensity = light_details[i].a; 72 + 73 + vec3 light_normal; 74 + if (light_positions[i].w == 1.0) { 75 + // Directional light. 76 + light_normal = light_positions[i].xyz; 77 + } else { 78 + vec3 light_dir = light_positions[i].xyz - vert_position.xyz; 79 + float light_dist = length(light_dir); 80 + light_normal = light_dir / light_dist; 81 + // Distance attenuation. 82 + light_intensity /= (light_dist * light_dist); 83 + } 84 + 85 + float diffuse_factor = dot(world_normal, light_normal); 86 + if (diffuse_factor > 0.0) { 87 + // Diffuse color. 88 + light_acc += unlit_rgb * diffuse_factor * light_color * light_intensity; 89 + 90 + // Blinn-Phong reflection model. 91 + float roughness = texture2D(roughness_map, vert_texcoord).x; 92 + if (roughness > 0.0) { 93 + float shininess = 2.0 / pow(roughness, 4.0) - 2.0; 94 + vec3 h = normalize(light_normal + view_normal); 95 + float specular_angle = max(dot(h, world_normal), 0.0); 96 + float specular_factor = pow(specular_angle, shininess); 97 + 98 + // Specular color. 99 + light_acc += unlit_rgb * specular_factor * light_color * light_intensity; 100 + } 101 + } 102 + } 103 + 104 + gl_FragColor = vec4(light_acc, 1.0); 105 + } 106 + `; 107 + 108 + export function mat1_mapped(gl: WebGLRenderingContext): Material<MappedLayout> { 109 + let program = link(gl, vertex, fragment); 110 + return { 111 + Mode: GL_TRIANGLES, 112 + Program: program, 113 + Locations: { 114 + Pv: gl.getUniformLocation(program, "pv")!, 115 + World: gl.getUniformLocation(program, "world")!, 116 + Self: gl.getUniformLocation(program, "self")!, 117 + 118 + DiffuseColor: gl.getUniformLocation(program, "diffuse_color")!, 119 + DiffuseMap: gl.getUniformLocation(program, "diffuse_map")!, 120 + NormalMap: gl.getUniformLocation(program, "normal_map")!, 121 + RoughnessMap: gl.getUniformLocation(program, "roughness_map")!, 122 + 123 + Eye: gl.getUniformLocation(program, "eye")!, 124 + LightPositions: gl.getUniformLocation(program, "light_positions")!, 125 + LightDetails: gl.getUniformLocation(program, "light_details")!, 126 + 127 + VertexPosition: gl.getAttribLocation(program, "attr_position")!, 128 + VertexTexCoord: gl.getAttribLocation(program, "attr_texcoord")!, 129 + VertexNormal: gl.getAttribLocation(program, "attr_normal")!, 130 + VertexTangent: gl.getAttribLocation(program, "attr_tangent")!, 131 + VertexBitangent: gl.getAttribLocation(program, "attr_bitangent")!, 132 + }, 133 + }; 134 + }
textures/Bricks059_1K_AmbientOcclusion.jpg

This is a binary file and will not be displayed.

textures/Bricks059_1K_AmbientOcclusion.jpg.webp

This is a binary file and will not be displayed.

textures/Bricks059_1K_Color.jpg

This is a binary file and will not be displayed.

textures/Bricks059_1K_Color.jpg.webp

This is a binary file and will not be displayed.

textures/Bricks059_1K_Displacement.jpg

This is a binary file and will not be displayed.

textures/Bricks059_1K_Displacement.jpg.webp

This is a binary file and will not be displayed.

textures/Bricks059_1K_Normal.jpg

This is a binary file and will not be displayed.

textures/Bricks059_1K_Normal.jpg.webp

This is a binary file and will not be displayed.

textures/Bricks059_1K_Roughness.jpg

This is a binary file and will not be displayed.

textures/Bricks059_1K_Roughness.jpg.webp

This is a binary file and will not be displayed.

+7 -2
textures/Makefile
··· 2 # On Ubuntu, install the `webp` package. 3 4 PNGS := $(wildcard *.png) 5 - WEBPS := $(PNGS:%.png=%.webp) 6 7 all: $(WEBPS) 8 9 - %.webp: %.png 10 @echo "$< → $@" 11 @cwebp -short -z 9 $< -o $@
··· 2 # On Ubuntu, install the `webp` package. 3 4 PNGS := $(wildcard *.png) 5 + JPGS := $(wildcard *.jpg) 6 + WEBPS := $(PNGS:%=%.webp) $(JPGS:%=%.webp) 7 8 all: $(WEBPS) 9 10 + %.png.webp: %.png 11 @echo "$< → $@" 12 @cwebp -short -z 9 $< -o $@ 13 + 14 + %.jpg.webp: %.jpg 15 + @echo "$< → $@" 16 + @cwebp -short $< -o $@
textures/Sponge001_1K_AmbientOcclusion.jpg

This is a binary file and will not be displayed.

textures/Sponge001_1K_AmbientOcclusion.jpg.webp

This is a binary file and will not be displayed.

textures/Sponge001_1K_Color.jpg

This is a binary file and will not be displayed.

textures/Sponge001_1K_Color.jpg.webp

This is a binary file and will not be displayed.

textures/Sponge001_1K_Displacement.jpg

This is a binary file and will not be displayed.

textures/Sponge001_1K_Displacement.jpg.webp

This is a binary file and will not be displayed.

textures/Sponge001_1K_Normal.jpg

This is a binary file and will not be displayed.

textures/Sponge001_1K_Normal.jpg.webp

This is a binary file and will not be displayed.

textures/Sponge001_1K_Roughness.jpg

This is a binary file and will not be displayed.

textures/Sponge001_1K_Roughness.jpg.webp

This is a binary file and will not be displayed.

textures/Sponge001_1K_Scattering.jpg

This is a binary file and will not be displayed.

textures/Sponge001_1K_Scattering.jpg.webp

This is a binary file and will not be displayed.

textures/checker1.webp textures/checker1.png.webp
textures/fire.webp textures/fire.png.webp
textures/kulka.png

This is a binary file and will not be displayed.

textures/kulka.webp

This is a binary file and will not be displayed.