GLSL 编程/Blender/纹理层
本教程介绍了**多纹理映射**,即在着色器中使用多个纹理图像。
它扩展了纹理球教程中的着色器代码,使其支持多个纹理,并展示了将它们组合在一起的方法。如果你还没有读过这些教程,现在就是个好机会。
许多真实表面(例如左侧图像中的人类皮肤)由几层不同颜色、透明度、反射率等组成。如果最顶层是不透明的并且不透光,那么渲染表面时实际上并不重要。然而,在许多情况下,最顶层是(半)透明的,因此对表面的准确渲染必须考虑多层。
实际上,包含在 Phong 反射模型中的镜面反射(参见镜面高光教程)通常对应于一个透明层,它会反射光线:人类皮肤上的汗水、水果上的蜡、嵌入色素颗粒的透明塑料等等。另一方面,漫反射对应于最顶层透明层下方的层。
照亮这种分层表面不需要几何模型来表示这些层:它们可以用一个单一的、无限薄的多边形网格来表示。然而,照明计算必须对不同的层计算不同的反射,并且必须考虑光在层之间的传输(当光进入层时和当光离开层时)。这种方法的例子包括 Nvidia 的“Dawn”演示(参见“GPU Gems”一书的第 3 章,该书可以在网上获取)以及 Nvidia 的“Human Head”演示(参见“GPU Gems 3”一书的第 14 章,该书也可以在网上获取)。
本教程无法完整描述这些过程。只需说,层通常与纹理图像相关联,以指定它们的特征。这里,我们只展示了如何使用两种纹理和一种特定的组合方式。实际上,这个例子与层无关,因此说明了多纹理映射除了表面层之外还有更多应用。
由于人类活动,地球的阴暗面并不完全黑暗。相反,人造灯光标志着城市的位置和延伸,如左侧图像所示。因此,地球的漫射光照不应该仅仅使阳光照射表面的纹理图像变暗,而应该将其与阴暗面的纹理图像混合。请注意,阳光照射的地球远比阴暗面的人造灯光亮;然而,我们降低了这种对比度,以便显示夜间纹理。
着色器代码扩展了纹理球教程中的代码,使其支持两种纹理图像,并使用漫射反射教程中描述的计算方法来处理单个方向光源
根据这个公式,漫射光照的级别 levelOfLighting
是 max(0, N·L)。然后,我们根据 levelOfLighting
混合白天纹理和夜间纹理的颜色。这可以通过将白天颜色乘以 levelOfLighting
,将夜间颜色乘以 1.0 - levelOfLighting
,然后将它们加在一起来确定片段的颜色来实现。或者,可以使用内置的 GLSL 函数 mix
(mix(a, b, w) = b*w + a*(1.0-w)
),这很可能更有效。因此,片段着色器可以是(同样使用我们特定的 longitudeLatitude
中的纹理坐标计算)
void main()
{
vec2 longitudeLatitude = vec2(
(atan(texCoords.y, texCoords.x)/3.1415926+1.0)*0.5,
1.0 - acos(texCoords.z) / 3.1415926);
nighttimeColor =
texture2D(nighttimeTexture, longitudeLatitude);
daytimeColor =
texture2D(daytimeTexture, longitudeLatitude);
gl_FragColor = mix(nighttimeColor, daytimeColor,
levelOfLighting);
// = daytimeColor * levelOfLighting
// + nighttimeColor * (1.0 - levelOfLighting)
}
请注意,这种混合与透明度教程中讨论的 alpha 混合非常相似,只是我们在片段着色器内部执行混合,并使用 levelOfLighting
而不是应该混合到另一个纹理之上的纹理的 alpha 分量(即不透明度)。实际上,如果 daytimeTexture
指定了 alpha 分量(参见透明纹理教程),我们可以使用这个 alpha 分量来将 daytimeTexture
混合到 nighttimeTexture
上。这对应于一个部分透明的层,它位于一个不透明层的顶部,该不透明层在最顶层透明的地方可见。
着色器代码将 gl_FrontMaterial.emission
按分量乘以夜间纹理的纹理颜色,以便可以通过更改材质选项卡中阴影 > 发射的值来控制其整体亮度。此外,光源的颜色 gl_LightSource[0].diffuse
也按分量乘以最终颜色,以便考虑彩色光源。
import bge
cont = bge.logic.getCurrentController()
VertexShader = """
varying float levelOfLighting; // the level of diffuse
// lighting that is computed in the vertex shader
varying vec4 texCoords;
void main()
{
vec3 normalDirection =
normalize(gl_NormalMatrix * gl_Normal);
vec3 lightDirection;
if (0.0 == gl_LightSource[0].position.w)
// directional light?
{
lightDirection =
normalize(vec3(gl_LightSource[0].position));
}
else // point light or spotlight (or other kind of light)
{
vec3 vertexToLightSource =
vec3(gl_LightSource[0].position
- gl_ModelViewMatrix * gl_Vertex);
lightDirection = normalize(vertexToLightSource);
}
levelOfLighting =
max(0.0, dot(normalDirection, lightDirection));
texCoords = gl_MultiTexCoord0;
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
"""
FragmentShader = """
varying float levelOfLighting;
varying vec4 texCoords;
uniform sampler2D daytimeTexture;
uniform sampler2D nighttimeTexture;
void main()
{
vec2 longitudeLatitude = vec2(
(atan(texCoords.y, texCoords.x)/3.1415926+1.0)*0.5,
1.0 - acos(texCoords.z) / 3.1415926);
vec4 nighttimeColor =
texture2D(nighttimeTexture, longitudeLatitude)
* gl_FrontMaterial.emission;
vec4 daytimeColor =
texture2D(daytimeTexture, longitudeLatitude);
gl_FragColor = mix(nighttimeColor, daytimeColor,
levelOfLighting) * gl_LightSource[0].diffuse;
// = daytimeColor * levelOfLighting
// + nighttimeColor * (1.0 - levelOfLighting) * ...
}
"""
mesh = cont.owner.meshes[0]
for mat in mesh.materials:
shader = mat.getShader()
if shader != None:
if not shader.isValid():
shader.setSource(VertexShader, FragmentShader, 1)
shader.setSampler('daytimeTexture', 0)
shader.setSampler('nighttimeTexture', 1)
使用这个着色器时,你必须确保白天纹理位于材质选项卡 > 纹理列表中的第一个位置,而夜间纹理位于第二个位置。否则,你必须调整 Python 脚本最后两行中的整数。
恭喜!你已经完成了最后一个关于基础纹理映射的教程。我们已经了解了
- 表面层如何影响材质的外观(例如人皮肤、打蜡的水果、塑料等等)
- 如何在对代表地球的球体进行纹理映射时考虑阴暗面的人造灯光。
- 如何在着色器中实现这种技术。
- 这与将 alpha 纹理混合到另一个不透明纹理之上的关系。
如果你还想了解更多
- 关于基础纹理映射,你应该阅读纹理球教程。
- 关于漫射反射,你应该阅读漫射反射教程。
- 关于 alpha 纹理,你应该阅读透明纹理教程。
- 关于高级皮肤渲染,你可以阅读 Randima Fernando(编辑)出版的《GPU Gems》一书中的第 3 章“‘Dawn’ 演示中的皮肤” by Curtis Beeson and Kevin Bjorke,该书由 Addison-Wesley 于 2004 年出版,可以在网上获取,以及 Hubert Nguyen(编辑)出版的《GPU Gems 3》一书中的第 14 章“用于逼真的实时皮肤渲染的高级技术” by Eugene d’Eon and David Luebke,该书由 Addison-Wesley 于 2007 年出版,也可以在网上获取。