Shader 首先,在本文开始前,我们先普及一下材质的概念,这里推荐材质,普及材质的内容都是截取自该网站,我觉得他写的已经够好了。在开始普及概念前,推荐一首我此刻想到的歌《光---陈粒》。 在真实世界里,每个物体会对光产生不同的反应。钢看起来比陶瓷花瓶更闪闪发光,一个木头箱子不会像钢箱子一样对光产生很强的反射。每个物体对镜面高光也有不同的反应。有些物体不会散射(Scatter)很多光却会反射(Reflect)很多光,结果看起来就有一个较小的高光点(Highlight),有些物体散射了很多,它们就会产生一个半径更大的高光。如果我们想要在OpenGL中模拟多种类型的物体,我们必须为每个物体分别定义材质(Material)属性。
我们指定一个物体和一个光的颜色来定义物体的图像输出,并使之结合环境(Ambient)和镜面强度(Specular Intensity)元素。当描述物体的时候,我们可以使用3种光照元素:环境光照(Ambient Lighting)、漫反射光照(Diffuse Lighting)、镜面光照(Specular Lighting)定义一个材质颜色。通过为每个元素指定一个颜色,我们已经对物体的颜色输出有了精密的控制。现在把一个镜面高光元素添加到这三个颜色里,这是我们需要的所有材质属性:
structMaterial
{
vec3 ambient;
vec3 diffuse;
vec3 specular;floatshininess;
};
以上是对材质的一个最简单概括,我们下面进入Cesium的环节。先来看看Cesium在Shader中对Material的定义:
struct czm_material
{
vec3 diffuse;floatspecular;floatshininess;
vec3 normal;
vec3 emission;floatalpha;
};
和上面给出的结构体大致相同,区别是少了环境光ambient,但多了法向量normal,自发光emission和alpha,我们带着这个疑问看一下Cesium处理材质的片段着色器:
varying vec3 v_positionEC;
varying vec3 v_normalEC;voidmain()
{
vec3 positionToEyeEC= -v_positionEC;
vec3 normalEC=normalize(v_normalEC);
#ifdef FACE_FORWARD
normalEC= faceforward(normalEC, vec3(0.0, 0.0, 1.0), -normalEC);
#endif
czm_materialInput materialInput;
materialInput.normalEC=normalEC;
materialInput.positionToEyeEC=positionToEyeEC;
czm_material material=czm_getDefaultMaterial(materialInput);
gl_FragColor=czm_phong(normalize(positionToEyeEC), material);
}
此时的坐标系是以相机为中心点,首先获取当前点的位置和法向量,通过czm_getMaterial获取默认的一个材质对象,gl_FragColor通过czm_phong方法得到对应的颜色。对于phong,在OpenGL SuperBible里面有详细的说明,大概就是通过material的属性,根据光的位置和光的颜色,最终计算出在该点当前环境和自身材质的影响下对应的颜色。我们来看看czm_phong的实现:
vec4 czm_phong(vec3 toEye, czm_material material)
{float diffuse = czm_private_getLambertDiffuseOfMaterial(vec3(0.0, 0.0, 1.0), material);if (czm_sceneMode ==czm_sceneMode3D) {
diffuse+= czm_private_getLambertDiffuseOfMaterial(vec3(0.0, 1.0, 0.0), material);
}float specular = czm_private_getSpecularOfMaterial(czm_sunDirectionEC, toEye, material) +czm_private_getSpecularOfMaterial(czm_moonDirectionEC, toEye, material);
vec3 materialDiffuse= material.diffuse * 0.5;
vec3 ambient=materialDiffuse;
vec3 color= ambient +material.emission;
color+= materialDiffuse *diffuse;
color+= material.specular *specular;returnvec4(color, material.alpha);
}
如上是phong颜色计算的算法,我并没有给出getLambertDiffuse和getSpecular的具体代码,都是光的基本物理规律。这里要说的是getLambertDiffuse的参数,如果是球面物体时,会调用czm_private_phong,此时参数为czm_sunDirectionEC,也就是太阳的位置,而这里认为光源的位置是靠近相机的某一个点,另外,环境光ambient默认是反射光的一半,这个也说的过去,最后我们看到最终颜色的alpha位是material.alpha。
上面是Shader中涉及到材质的一个