门口站满了人,走廊里也站了人。有人从外地坐夜车来的,背包没放就进了教室。许知意在签到处发名牌,发到三百八十号的时候,牌子不够了。她撕了一沓空白贴纸,手写编号。一个学生递过身份证,她写了个“381”贴在他胸口。那人低头看了一眼,笑着说有编号了。
沈连河站在讲台后面,笔记本翻开。粉笔从盒子里拿出来,摆了三根。今天讲实时渲染的光照模型。台下四百双眼睛看着他。前排有人举着手机录像,有人已经打开了笔记本准备画图。
他转身,在黑板上画了一个球。
不是圆,是球。用阴影表现出立体感。他画了四笔,最后一笔在球的底部擦了擦,让暗部过渡更自然。粉笔断了。他换了一根。
“漫反射。兰伯特模型。光是均匀散射的。不看摄像机位置,只看光源方向。”
他在球上画箭头。光线从左上角来,球面亮度从亮到暗渐变。箭头画了四条,每条都标了角度。
“公式是漫反射系数乘以光源强度乘以法线和光源方向的点积。点积大于零才亮,小于零就全黑。”
台下有人举手。
“老师,点积是负的时候,要不要取绝对值。”
“不要。取绝对值就变成从背面照亮了。物理上不对。”
提问的人在本子上记。沈连河等他写完,画第二个球。
“镜面反射。布林-冯模型。光集中在反射方向。高光区域小,亮度高。”
他把高光点画在球的右上角。用粉笔侧面擦了擦,让高光边缘模糊。退后一步看了看,又加了一笔。
“高光的大小由粗糙度控制。粗糙度低,高光小且亮。粗糙度高,高光分散。代码里用一个参数,范围0到1。0是镜子,1是磨砂。”
又一个人举手。“老师,布林-冯和冯氏模型有什么区别。”
“计算量。布林-冯用半角向量,冯氏用反射方向。布林-冯更快,效果差不多。游戏里用布林-冯多。”
第三个球。环境光。整个球均匀地亮了一点点。他用粉笔轻轻涂了一层灰色,涂得很薄,能看到黑板底色。
“环境光。模拟场景中多次反射的间接光照。没有环境光,背光面会完全黑掉。不自然。”
他退后一步,看那三个球。退后两步,又看了一遍。
“还有吗。再来一个。”
第四个球。三个模型叠加。漫反射打底,镜面反射加高光,环境光提亮暗部。画完的时候,粉笔又断了一次。他弯腰捡起来,断头太短,扔了。换了第四根。
“这个球,就是你在游戏里看到的效果。所有复杂光照,都是这三个模型的不同组合。光追也是。蒙特卡洛积分算的是光路,光照模型还是这几个。”
台下安静了。他站在讲台后面,粉笔灰落在袖子上。衬衫袖口蹭了一道白印。
后排有人举手。
“老师,这些模型是二十年前的。现在还用吗。”
“用。所有复杂光照模型,都是这些简单模型的组合。光追也是。蒙特卡洛积分是采样,光照模型还是这些。基础不会过时。”
提问的人点头。坐下了。
前排一个男生举手。“老师,你的笔记本能看看吗。”
沈连河拿起笔记本,走下讲台。他把笔记本递给第一排的人。那个人翻开,一页一页看。兰伯特,布林-冯,阴影映射,屏幕空间反射,光线追踪。看到第三十页的时候,手停了一下。纸上画满了箭头和公式,边角贴着透明胶。他翻到最后一页,那行铅笔字。“让世界用我写的渲染器看世界。”他看了几秒,把笔记本递还给沈连河。旁边的人伸头看了一眼,也没说话。后排有人站起来想看,但没挤过来。
沈连河走回讲台,把笔记本放下。
“还有问题吗。”
没人举手。台下开始鼓掌。不是客气的鼓掌,是一直在鼓。沈连河站在讲台后面,笔记本抱在怀里。掌声响了十几秒,他点了下头,转身擦黑板。粉笔灰飘起来,呛了一下。他咳了一声,继续擦。
散课后,沈连河被围住了。这一次人比上次多,围了三层。有人问GTX 400的光追单元什么时候能买到,有人问引擎3.0的Shader编译器能不能支持自定义函数,有人问开发者大学下一堂课讲什么。他一个一个答。
一个学生挤到前面,手里拿着笔记本。不是贴透明胶的那本,是新的。翻开着,里面画满了图。沈连河接过笔记本翻了翻。看到一页示意图,手画的,线条很直,像是用尺子比着画的。角落里有一行小字。“我也不知道画得对不对。”
沈连河看着这行字看了几秒。
“对。不用尺子画。手感更重要。线条直不直不重要,光的方向要对。”
学生点头。他把笔记本合上,