一、应用场景
如果想实现不给Mesh上材质,且还想让Mesh有颜色,那么可以考虑使用顶点着色方案。通常应用与一下场景:
- Mesh全合并后,上色问题。比如rvt/ifc格式有很多mesh,会引起dc问题,所以可以全部合并Mesh,后通过顶点上色的方案优化帧率。
知识点:在WebGL中,顶点法线是一个重要的属性,它用于光照计算。通过插值顶点法线,可以实现平滑着色(如Phong或Blinn-Phong着色),使得物体表面看起来更加逼真和平滑。
1. 顶点法线的作用
顶点法线(Vertex Normal)的主要用途是计算光照。在Phong光照模型等 shading 过程中,法线决定了光线与表面如何交互(如漫反射、镜面反射的计算)。若没有法线数据,着色器无法正确计算光照效果。
2. 平滑着色(Smooth Shading)的实现
顶点法线确实可以影响着色平滑度,但关键在于法线的插值方式:
- 平滑着色(如Gouraud或Phong shading):顶点法线在不同顶点之间被线性插值,导致颜色渐变,形成平滑外观。通常需要共享顶点的法线取平均值(避免硬边缘)。
- 平坦着色(Flat Shading):每个面的法线相同,导致棱角分明的外观。
因此,顶点法线是平滑着色的必要条件,但还需配合以下条件:
- 正确的法线插值(由渲染管线自动完成)。
- 适当的法线数据(如平均化处理,避免硬边缘)。
3. 常见误解
- 法线 ≠ 颜色插值:法线本身不直接“解决”着色问题,而是通过光照计算间接影响颜色。
- 着色器的作用:顶点着色器传递法线,片段着色器最终计算颜色。平滑与否取决于两者的协作。
二、技术测试
下面的代码,创建了8个顶点,给顶点分别上色后,实现了各个三角面的固定颜色、渐变色效果。

var createScene = function () {
var scene = new BABYLON.Scene(engine);
var light = new BABYLON.DirectionalLight("direct", new BABYLON.Vector3(0, 0, 10), scene);
var light1 = new BABYLON.DirectionalLight("direct", new BABYLON.Vector3(0, 0, -10), scene);
var camera = new BABYLON.ArcRotateCamera("camera1", 0, 0, 0, new BABYLON.Vector3(0, 0, 0), scene);
camera.setPosition(new BABYLON.Vector3(0, 5, -30));
camera.attachControl(canvas, true);
var mat = new BABYLON.StandardMaterial('mat'); //这个StandardMaterial 主要打开背面剔除,没其它作用(不显示背面的话,不要也行)
//mat.diffuseColor = new BABYLON.Color3(0, 1, 1); //其漫反射颜色(diffuseColor)直接决定物体基础色。
mat.backFaceCulling = false; //打开背面剔除
mat.vertexColors = true;
//Create a custom mesh
var customMesh = new BABYLON.Mesh("custom", scene);
customMesh.material = mat;
//Set arrays for positions and indices
var positions = [-5, 2, -3, -7, -2, -3, -3, -2, -3, 5, 2, 3, 7, -2, 3, 3, -2, 3, -5, 2, -3, -3, -2, -3, 0, 0, -3]; //9个顶点
var indices = [0, 1, 2, 3, 4, 5, 6, 7, 8]; //3个三角形
var colors = [1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]; //9个顶点,各个顶点的RGBA值
//Empty array to contain calculated values
var normals = [];
var vertexData = new BABYLON.VertexData();
BABYLON.VertexData.ComputeNormals(positions, indices, normals);
//Assign positions, indices and normals to vertexData
vertexData.positions = positions;
vertexData.indices = indices;
vertexData.normals = normals;
vertexData.colors = colors;
//Apply vertexData to custom mesh
vertexData.applyToMesh(customMesh);
return scene;
};