内容参考闫令琪课程《games202-高质量实时渲染及作业》
games202 所有的练习作业地址:
https://games-cn.org/forums/topic/games202zuoyehuizong/
第一篇来熟悉作业框架,实现简单的phong光照(冯氏光照),功能基于前端框架Three.js实现
实现效果
代码基于前端语言实现,需要本地开一个服务器,能解析js,我这里使用python自带的server,在工程根目录执行 python -m http.server,浏览器访问localhost://8000
python -m http.server
# Serving HTTP on :: port 8000 (http://[::]:8000/) ...
场景中有一个小女孩模型marry + 旋转的点光源(白色光)。默认不加光照效果,用模型的颜色渲染,没有明暗区分
增加phong光照后,模型有明暗变化
代码结构
打开下载好的代码框架,能看到以下文件
每个文件夹说明
程序执行流程
整个程序非常简洁,很方便阅读、学习,应该也参考了实际工程的代码架构。
最核心的逻辑如下:
- 入口engine.js:管理渲染的初始化、场景加载、渲染循环
- 渲染器WebglRenderer.js:承载了场景的功能,包含所有可渲染的网格对象
- 加载器loadOBJ.js:加载网格模型和材质
- 网格MeshRender.js:包含网格数据Mesh.js和每个网格的材质
- Material.js:封装了Shader的接口和参数
- shader.js:封装了着色器的加载、编译
实现Phong光照
Phong shader的代码框在assignment.pdf中已经给出了,参照说明更新代码,可以得到开头的效果
难点说明
稍微难一点的逻辑在于,程序中创建shader时,将需要的uniforms、attribs统一管理起来。
shader.js
addShaderLocations(result, shaderLocations) {
const gl = this.gl;
result.uniforms = {};
result.attribs = {};
if (shaderLocations && shaderLocations.uniforms && shaderLocations.uniforms.length) {
for (let i = 0; i < shaderLocations.uniforms.length; ++i) {
result.uniforms = Object.assign(result.uniforms, {
[shaderLocations.uniforms[i]]: gl.getUniformLocation(result.glShaderProgram, shaderLocations.uniforms[i]),
});
//console.log(gl.getUniformLocation(result.glShaderProgram, 'uKd'));
}
}
if (shaderLocations && shaderLocations.attribs && shaderLocations.attribs.length) {
for (let i = 0; i < shaderLocations.attribs.length; ++i) {
result.attribs = Object.assign(result.attribs, {
[shaderLocations.attribs[i]]: gl.getAttribLocation(result.glShaderProgram, shaderLocations.attribs[i]),
});
}
}
return result;
}
每次循环时,更新uniforms和attribs
MeshRender.js,根据参数类型调用不同的gl接口
for (let k in this.material.uniforms) {
if (this.material.uniforms[k].type == 'matrix4fv') {
gl.uniformMatrix4fv(
this.shader.program.uniforms[k],
false,
this.material.uniforms[k].value);
} else if (this.material.uniforms[k].type == '3fv') {
gl.uniform3fv(
this.shader.program.uniforms[k],
this.material.uniforms[k].value);
} else if (this.material.uniforms[k].type == '1f') {
gl.uniform1f(
this.shader.program.uniforms[k],
this.material.uniforms[k].value);
} else if (this.material.uniforms[k].type == '1i') {
gl.uniform1i(
this.shader.program.uniforms[k],
this.material.uniforms[k].value);
} else if (this.material.uniforms[k].type == 'texture') {
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, this.material.uniforms[k].value.texture);
gl.uniform1i(this.shader.program.uniforms[k], 0);
}
}
补充
浏览器里debug
F12打开调试界面,进入Sources栏,找到要断点的代码,点击添加debug point,command + R触发刷新进入断点处
有两个按钮,一个是单步,一个是跳过
修改代码无效
浏览器可能有缓存,清除缓存再刷新
参考
- GAMES202作业1框架说明
https://blog.youkuaiyun.com/qq_41835314/article/details/125619239?spm=1001.2014.3001.5502
- Three.js 导入OBJ格式和MTL的格式
https://blog.youkuaiyun.com/qq_30100043/article/details/79618258
- GAMES202作业1-模型不可见修复
https://games-cn.org/forums/topic/zuoye0-jieguobuwendingyoushimoxingxianshibuquan/