GLSL 编程/Unity/纹理层
本教程介绍了 **多纹理**,即在着色器中使用多个纹理图像。
它扩展了 “纹理球体”部分 的着色器代码到多个纹理,并展示了一种组合它们的方法。如果您还没有阅读过该教程,这是一个很好的机会去阅读它。
许多真实的表面(例如左边图像中所示的人类皮肤)由几层不同颜色、透明度、反射率等组成。如果最顶层是不透明的,并且不透射任何光线,那么对于渲染表面来说并不重要。然而,在许多情况下,最顶层是(半)透明的,因此表面的精确渲染必须考虑到多层。
事实上,包含在 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)
),这可能更有效。因此,片段着色器可能是
#ifdef FRAGMENT
void main()
{
vec4 nighttimeColor = _Color
* texture2D(_MainTex, vec2(textureCoordinates));
vec4 daytimeColor = _LightColor0
* texture2D(_DecalTex, vec2(textureCoordinates));
gl_FragColor =
mix(nighttimeColor, daytimeColor, levelOfLighting);
// = daytimeColor * levelOfLighting
// + nighttimeColor * (1.0 - levelOfLighting)
}
#endif
请注意,这种混合与在 “透明度”部分 中讨论的 alpha 混合非常相似,除了我们在片段着色器中执行混合,并使用 levelOfLighting
而不是纹理的 alpha 分量(即不透明度),该纹理应该混合到另一个纹理的“上面”。事实上,如果 _DecalTex
指定了一个 alpha 分量(见 “透明纹理”部分),我们可以使用这个 alpha 分量来混合 _DecalTex
到 _MainTex
上。这实际上是 Unity 的标准 Decal
着色器所做的,它对应于一个层,该层在可见的透明层的顶部部分透明。
着色器属性的名称选择与回退着色器的属性名称一致 - 在这种情况下是 Decal
着色器(注意,回退 Decal
着色器和标准 Decal
着色器似乎以相反的方式使用这两个纹理)。此外,还引入了另一个属性 _Color
,它与夜晚纹理的纹理颜色(按分量)相乘,以控制其整体亮度。此外,光源的颜色 _LightColor0
也与白天纹理的颜色(按分量)相乘,以考虑彩色光源。
Shader "GLSL multitexturing of Earth" {
Properties {
_DecalTex ("Daytime Earth", 2D) = "white" {}
_MainTex ("Nighttime Earth", 2D) = "white" {}
_Color ("Nighttime Color Filter", Color) = (1,1,1,1)
}
SubShader {
Pass {
Tags { "LightMode" = "ForwardBase" }
// pass for the first, directional light
GLSLPROGRAM
uniform sampler2D _MainTex;
uniform sampler2D _DecalTex;
uniform vec4 _Color;
// The following built-in uniforms (except _LightColor0)
// are also defined in "UnityCG.glslinc",
// i.e. one could #include "UnityCG.glslinc"
uniform mat4 _Object2World; // model matrix
uniform mat4 _World2Object; // inverse model matrix
uniform vec4 _WorldSpaceLightPos0;
// direction to or position of light source
uniform vec4 _LightColor0;
// color of light source (from "Lighting.cginc")
varying float levelOfLighting;
// level of diffuse lighting computed in vertex shader
varying vec4 textureCoordinates;
#ifdef VERTEX
void main()
{
mat4 modelMatrix = _Object2World;
mat4 modelMatrixInverse = _World2Object; // unity_Scale.w
// is unnecessary because we normalize vectors
vec3 normalDirection = normalize(vec3(
vec4(gl_Normal, 0.0) * modelMatrixInverse));
vec3 lightDirection;
if (0.0 == _WorldSpaceLightPos0.w) // directional light?
{
lightDirection = normalize(vec3(_WorldSpaceLightPos0));
}
else // point or spot light
{
lightDirection = vec3(0.0, 0.0, 0.0);
// ignore other light sources
}
levelOfLighting =
max(0.0, dot(normalDirection, lightDirection));
textureCoordinates = gl_MultiTexCoord0;
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
#endif
#ifdef FRAGMENT
void main()
{
vec4 nighttimeColor = _Color
* texture2D(_MainTex, vec2(textureCoordinates));
vec4 daytimeColor = _LightColor0
* texture2D(_DecalTex, vec2(textureCoordinates));
gl_FragColor =
mix(nighttimeColor, daytimeColor, levelOfLighting);
// = daytimeColor * levelOfLighting
// + nighttimeColor * (1.0 - levelOfLighting)
}
#endif
ENDGLSL
}
}
// The definition of a fallback shader should be commented out
// during development:
// Fallback "Decal"
}
运行此着色器时,请确保在场景中启用了方向光源。
恭喜!您已经完成了关于基本纹理的最后一个教程。我们已经了解了
- 表面层如何影响材料的外观(例如人皮、打蜡的水果、塑料等)
- 在纹理化代表地球的球体时,如何考虑阴暗面的人造灯光。
- 如何在着色器中实现这种技术。
- 这与将 alpha 纹理混合到第二个不透明纹理上的关系。
如果您仍然想了解更多
- 关于基本纹理,您应该阅读 “纹理球体”部分。
- 关于漫反射,您应该阅读 “漫反射”部分。
- 关于 alpha 纹理,您应该阅读 “透明纹理”部分。
- 关于高级皮肤渲染,您可以阅读 Randima Fernando(编辑)于 2004 年由 Addison-Wesley 出版、由 Curtis Beeson 和 Kevin Bjorke 撰写的“GPU Gems”一书中的第 3 章“‘Dawn’ 演示中的皮肤”,该书可在 在线 获取,以及 Hubert Nguyen(编辑)于 2007 年由 Addison-Wesley 出版、由 Eugene d’Eon 和 David Luebke 撰写的“GPU Gems 3”一书中的第 14 章“逼真的实时皮肤渲染的高级技术”,该书也可在 在线 获取。