开个新坑,之前囫囵吞枣地过了一遍 202,现在忘得差不多了,该捡起来。正好给枯燥的生活增添一点色彩(?),最近比较忙但还是争取一周做一个作业
🚩 0x00 To begin with
这篇文章将会包含以下内容:
- GAMES202 作业 0
- 不包含作业框架分析
For reference👇:
- 📺 B 站视频: GAMES202-高质量实时渲染
- 📦 代码仓库: congyuxiaoyoudao/GAMES202_Homework at working
master 分支上是 202 全部作业汇总,working 分支用于提交代码。需要原始作业可下载 master 分支的包
⚙️ 0x01 Configuration 环境配置
选择使用 VSCode 插件搭建本地服务器,在扩展中搜索 Live Server,安装此插件。

在编辑 Html 代码窗口按 F1(或者右键)选择 Live Server: Open with Live Server,随后会自动打开浏览器,导航至本地服务器端口,一切正常的话是下面这个效果:

可以使用鼠标和右上角的控制面板进行一些简单的交互。
如果遇到模型贴图加载不出来(只有 202 字样),可以选择多刷新几次。或者直接在加载脚本前预加载贴图,可以根治这个问题。
1<link rel="preload" href="assets/mary/MC003_Kozakura_Mari.png" as="image" type="image/png" crossorigin/>
🤔 0x02 Blinn-Phong Shading Model
没什么好说的,CV 工程师
使用 GLSL 编写 Shader
如果使用字符串的方式引入 Shader,就在 InternalShader.js 中加上:
1// Phong Shader
2
3const PhongVertexShader = `
4// attribute is vertex input data
5attribute vec3 aVertexPosition;
6attribute vec3 aNormalPosition;
7attribute vec2 aTextureCoord;
8
9// uniform is consistent/global data between VS/PS in a drawcall
10uniform mat4 uModelViewMatrix;
11uniform mat4 uProjectionMatrix;
12
13// varying is data need to be interpolated from VS to PS
14varying highp vec2 vTextureCoord;
15varying highp vec3 vFragPos;
16varying highp vec3 vNormal;
17
18void main(void) {
19
20 vFragPos = aVertexPosition;
21 vNormal = aNormalPosition;
22 // MVP
23 gl_Position = uProjectionMatrix * uModelViewMatrix * vec4(aVertexPosition, 1.0);
24
25 vTextureCoord = aTextureCoord;
26}
27`;
28
29const PhongFragmentShader = `
30#ifdef GL_ES
31precision mediump float;
32#endif
33uniform sampler2D uSampler;
34//binn
35uniform vec3 uKd;
36uniform vec3 uKs;
37uniform vec3 uLightPos;
38uniform vec3 uCameraPos;
39uniform float uLightIntensity;
40uniform int uTextureSample;
41
42varying highp vec2 vTextureCoord;
43varying highp vec3 vFragPos;
44varying highp vec3 vNormal;
45
46void main(void) {
47 vec3 color;
48 if (uTextureSample == 1) {
49 // Gamma correction
50 color = pow(texture2D(uSampler, vTextureCoord).rgb, vec3(2.2));
51 } else {
52 // default color
53 color = uKd;
54 }
55
56 vec3 ambient = 0.05 * color;
57
58 // lambertian diffuse
59 vec3 lightDir = normalize(uLightPos- vFragPos);
60 vec3 normal = normalize(vNormal);
61 float diff = max(dot(lightDir, normal), 0.0);
62 float light_atten_coff = uLightIntensity / length(uLightPos- vFragPos);
63 vec3 diffuse = diff * light_atten_coff * color;
64
65 // Phong specular
66 vec3 viewDir = normalize(uCameraPos- vFragPos);
67 float spec = 0.0;
68 vec3 reflectDir = reflect(-lightDir, normal);
69 spec = pow (max(dot(viewDir, reflectDir), 0.0), 35.0);
70 vec3 specular = uKs * light_atten_coff * spec;
71
72 // Compose all components and do gamma reverse
73 gl_FragColor = vec4(pow((ambient + diffuse + specular), vec3(1.0/2.2)), 1.0);
74
75}
76`;
实现 PhongMaterial 类
在 src/materials/ 下新建 PhongMaterial.js,定义继承自 Material 的 PhongMaterial 类,传参,绑定 Shader。
super调用父类构造
1class PhongMaterial extends Material {
2/**
3 * Creates an instance of PhongMaterial.
4 * @param {vec3f} color The material color
5 * @param {Texture} colorMap The texture object of the material
6 * @param {vec3f} specular The material specular coefficient
7 * @param {float} intensity The light intensity
8 * @memberof PhongMaterial
9 */
10 constructor(color, colorMap, specular, intensity) {
11 let textureSample = 0;
12
13 if (colorMap != null) {
14 textureSample = 1;
15 super({
16 'uTextureSample': { type: '1i', value: textureSample },
17 'uSampler': { type: 'texture', value: colorMap },
18 'uKd': { type: '3fv', value: color },
19 'uKs': { type: '3fv', value: specular },
20 'uLightIntensity': { type: '1f', value: intensity }
21 }, [], PhongVertexShader, PhongFragmentShader);
22 } else {
23 //console.log(color);
24 super({
25 'uTextureSample': { type: '1i', value: textureSample },
26
27 'uKd': { type: '3fv', value: color },
28 'uKs': { type: '3fv', value: specular },
29 'uLightIntensity': { type: '1f', value: intensity }
30 }, [], PhongVertexShader, PhongFragmentShader);
31 }
32
33 }
34}
导入 PhongMaterial.js
在 index.html 中,在导入 Material.js 之后导入 PhongMaterial.js。
1<script src="src/materials/Material.js" defer></script>
2<!-- Load phong material **after** Material -->
3<script src="src/materials/PhongMaterial.js" defer></script>
使用 PhongMaterial
src/loads/loadOBJ.js 中,删除原本创建默认材质的部分,改为使用 PhongMaterial 创建实例。
1// // MARK: You can change the myMaterial object to your own Material instance
2
3// let textureSample = 0;
4// let myMaterial;
5// if (colorMap != null) {
6// textureSample = 1;
7// myMaterial = new Material({
8// 'uSampler': { type: 'texture', value: colorMap },
9// 'uTextureSample': { type: '1i', value: textureSample },
10// 'uKd': { type: '3fv', value: mat.color.toArray() }
11// },[],VertexShader, FragmentShader);
12// }else{
13// myMaterial = new Material({
14// 'uTextureSample': { type: '1i', value: textureSample },
15// 'uKd': { type: '3fv', value: mat.color.toArray() }
16// },[],VertexShader, FragmentShader);
17// }
18
19let myMaterial = new PhongMaterial( mat.color.toArray(),
20 colorMap,
21 mat.specular.toArray(),
22 renderer.lights[0].entity.mat.intensity)
🍪 Extra:从文件中加载 Shader
为了让这篇显得不那么水,补一下另一个从外部文件加载 Shader 的方法,在 src/shaders/phongShader 中已经给出了这两个着色器文件
更改 PhongMaterial 类
因为构造函数不支持异步,就把创建 Material 的逻辑放到一个函数中,在这个函数里调用 loadShader.js 中定义的 loadShaderFile 函数加载 shader 文件,等待加载完后再调用构造函数并返回新创建的实例:
1// Load the shader files from file
2class PhongMaterial extends Material {
3 constructor(uniforms, vertexShader, fragmentShader) {
4 super(uniforms, [], vertexShader, fragmentShader);
5 }
6
7 /**
8 * Creates an instance of PhongMaterial async.
9 * @param {vec3f} color
10 * @param {Texture} colorMap
11 * @param {vec3f} specular
12 * @param {float} intensity
13 * @returns {Promise<PhongMaterial>}
14 */
15 static async create(color, colorMap, specular, intensity) {
16 const textureSample = colorMap ? 1 : 0;
17
18 const [vertexShader, fragmentShader] = await Promise.all([
19 loadShaderFile('src/shaders/phongShader/vertex.glsl'),
20 loadShaderFile('src/shaders/phongShader/fragment.glsl')
21 ]);
22 // console.log('Vertex Shader:', vertexShader.slice(0, 100));
23 // console.log('Fragment Shader:', fragmentShader.slice(0, 100));
24 const uniforms = {
25 'uTextureSample': { type: '1i', value: textureSample },
26 'uKd': { type: '3fv', value: color },
27 'uKs': { type: '3fv', value: specular },
28 'uLightIntensity': { type: '1f', value: intensity }
29 };
30
31 if (colorMap) {
32 uniforms['uSampler'] = { type: 'texture', value: colorMap };
33 }
34
35 return new PhongMaterial(uniforms, vertexShader, fragmentShader);
36 }
37}
更改 loadOBJ.js
用 create 方法创建一个新材质实例,完成后进行渲染。
1PhongMaterial.create( mat.color.toArray(),
2 colorMap,
3 mat.specular.toArray(),
4 renderer.lights[0].entity.mat.intensity)
5 .then((myMaterial) => {
6 let meshRender = new MeshRender(renderer.gl, mesh, myMaterial);
7 renderer.addMesh(meshRender);
8 })
📝 Afterwords
一切正常就是下面的效果。
