GLSL 编程/Unity/光泽纹理
本教程涵盖了 **部分光泽纹理表面的逐像素光照**。
它结合了 “纹理球体”部分 和 “平滑镜面高光”部分 的着色器代码,计算使用纹理的 RGB 分量确定漫反射材质颜色的逐像素光照,以及使用同一纹理的 A 分量确定镜面反射的强度。如果你没有阅读过这些部分,现在是一个很好的机会来阅读它们。
在 “光照纹理表面”部分 中,漫反射的材质常量由纹理图像的 RGB 分量决定。在这里,我们扩展了这种技术,并通过同一纹理图像的 A(alpha)分量来确定镜面反射的强度。仅使用一个纹理提供了显着的性能优势,特别是因为在某些情况下,RGBA 纹理查找与 RGB 纹理查找一样昂贵。
如果纹理图像的“光泽”(即镜面反射的强度)编码在 RGBA 纹理图像的 A(alpha)分量中,我们可以简单地将镜面反射的材质常量 与纹理图像的 alpha 分量相乘。 在 “镜面高光”部分 中介绍,并出现在 Phong 反射模型的镜面反射项中
如果乘以纹理图像的 alpha 分量,则该项达到最大值(即表面是光泽的),其中 alpha 为 1,并且为 0(即表面根本没有光泽),其中 alpha 为 0。
着色器代码是 “平滑镜面高光”部分 中的逐像素光照和 “纹理球体”部分 中的纹理的组合。类似于 “光照纹理表面”部分,纹理颜色 textureColor
的 RGB 分量乘以漫反射材质颜色 _Color
。
在左边的特定纹理图像中,alpha 分量对于水为 0,对于陆地为 1。但是,应该是水是光泽的,而陆地不是。因此,对于这个特定的图像,我们应该将镜面材质颜色乘以 (1.0 - textureColor.a)
。另一方面,通常的光泽贴图需要乘以 textureColor.a
。(注意对着色器程序进行这种改变是多么容易。)
Shader "GLSL per-pixel lighting with texture" {
Properties {
_MainTex ("RGBA Texture For Material Color", 2D) = "white" {}
_Color ("Diffuse Material Color", Color) = (1,1,1,1)
_SpecColor ("Specular Material Color", Color) = (1,1,1,1)
_Shininess ("Shininess", Float) = 10
}
SubShader {
Pass {
Tags { "LightMode" = "ForwardBase" }
// pass for ambient light and first light source
GLSLPROGRAM
// User-specified properties
uniform sampler2D _MainTex;
uniform vec4 _Color;
uniform vec4 _SpecColor;
uniform float _Shininess;
// The following built-in uniforms (except _LightColor0)
// are also defined in "UnityCG.glslinc",
// i.e. one could #include "UnityCG.glslinc"
uniform vec3 _WorldSpaceCameraPos;
// camera position in world space
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 vec4 position;
// position of the vertex (and fragment) in world space
varying vec3 varyingNormalDirection;
// surface normal vector in world space
varying vec4 textureCoordinates;
#ifdef VERTEX
void main()
{
mat4 modelMatrix = _Object2World;
mat4 modelMatrixInverse = _World2Object; // unity_Scale.w
// is unnecessary because we normalize vectors
position = modelMatrix * gl_Vertex;
varyingNormalDirection = normalize(vec3(
vec4(gl_Normal, 0.0) * modelMatrixInverse));
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
textureCoordinates = gl_MultiTexCoord0;
}
#endif
#ifdef FRAGMENT
void main()
{
vec3 normalDirection = normalize(varyingNormalDirection);
vec3 viewDirection =
normalize(_WorldSpaceCameraPos - vec3(position));
vec3 lightDirection;
float attenuation;
vec4 textureColor =
texture2D(_MainTex, vec2(textureCoordinates));
if (0.0 == _WorldSpaceLightPos0.w) // directional light?
{
attenuation = 1.0; // no attenuation
lightDirection = normalize(vec3(_WorldSpaceLightPos0));
}
else // point or spot light
{
vec3 vertexToLightSource =
vec3(_WorldSpaceLightPos0 - position);
float distance = length(vertexToLightSource);
attenuation = 1.0 / distance; // linear attenuation
lightDirection = normalize(vertexToLightSource);
}
vec3 ambientLighting = vec3(gl_LightModel.ambient)
* vec3(_Color) * vec3(textureColor);
vec3 diffuseReflection = attenuation * vec3(_LightColor0)
* vec3(_Color) * vec3(textureColor)
* max(0.0, dot(normalDirection, lightDirection));
vec3 specularReflection;
if (dot(normalDirection, lightDirection) < 0.0)
// light source on the wrong side?
{
specularReflection = vec3(0.0, 0.0, 0.0);
// no specular reflection
}
else // light source on the right side
{
specularReflection = attenuation * vec3(_LightColor0)
* vec3(_SpecColor) * (1.0 - textureColor.a)
// for usual gloss maps: "... * textureColor.a"
* pow(max(0.0, dot(
reflect(-lightDirection, normalDirection),
viewDirection)), _Shininess);
}
gl_FragColor = vec4(ambientLighting
+ diffuseReflection + specularReflection, 1.0);
}
#endif
ENDGLSL
}
Pass {
Tags { "LightMode" = "ForwardAdd" }
// pass for additional light sources
Blend One One // additive blending
GLSLPROGRAM
// User-specified properties
uniform sampler2D _MainTex;
uniform vec4 _Color;
uniform vec4 _SpecColor;
uniform float _Shininess;
// The following built-in uniforms (except _LightColor0)
// are also defined in "UnityCG.glslinc",
// i.e. one could #include "UnityCG.glslinc"
uniform vec3 _WorldSpaceCameraPos;
// camera position in world space
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 vec4 position;
// position of the vertex (and fragment) in world space
varying vec3 varyingNormalDirection;
// surface normal vector in world space
varying vec4 textureCoordinates;
#ifdef VERTEX
void main()
{
mat4 modelMatrix = _Object2World;
mat4 modelMatrixInverse = _World2Object; // unity_Scale.w
// is unnecessary because we normalize vectors
position = modelMatrix * gl_Vertex;
varyingNormalDirection = normalize(vec3(
vec4(gl_Normal, 0.0) * modelMatrixInverse));
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
textureCoordinates = gl_MultiTexCoord0;
}
#endif
#ifdef FRAGMENT
void main()
{
vec3 normalDirection = normalize(varyingNormalDirection);
vec3 viewDirection =
normalize(_WorldSpaceCameraPos - vec3(position));
vec3 lightDirection;
float attenuation;
vec4 textureColor =
texture2D(_MainTex, vec2(textureCoordinates));
if (0.0 == _WorldSpaceLightPos0.w) // directional light?
{
attenuation = 1.0; // no attenuation
lightDirection = normalize(vec3(_WorldSpaceLightPos0));
}
else // point or spot light
{
vec3 vertexToLightSource =
vec3(_WorldSpaceLightPos0 - position);
float distance = length(vertexToLightSource);
attenuation = 1.0 / distance; // linear attenuation
lightDirection = normalize(vertexToLightSource);
}
vec3 diffuseReflection = attenuation * vec3(_LightColor0)
* vec3(_Color) * vec3(textureColor)
* max(0.0, dot(normalDirection, lightDirection));
vec3 specularReflection;
if (dot(normalDirection, lightDirection) < 0.0)
// light source on the wrong side?
{
specularReflection = vec3(0.0, 0.0, 0.0);
// no specular reflection
}
else // light source on the right side
{
specularReflection = attenuation * vec3(_LightColor0)
* vec3(_SpecColor) * (1.0 - textureColor.a)
// for usual gloss maps: "... * textureColor.a"
* pow(max(0.0, dot(
reflect(-lightDirection, normalDirection),
viewDirection)), _Shininess);
}
gl_FragColor =
vec4(diffuseReflection + specularReflection, 1.0);
}
#endif
ENDGLSL
}
}
// The definition of a fallback shader should be commented out
// during development:
// Fallback "Specular"
}
对上面特定纹理图像的这个着色器进行一个有用的修改是,将漫反射材质颜色设置为 alpha 分量为 0 的深蓝色。
正如在 “平滑镜面高光”部分 中所讨论的那样,镜面高光通常使用每个顶点光照渲染得不好。但是,有时由于性能限制,别无选择。为了在 “光照纹理表面”部分 的着色器代码中包含光泽映射,两个通道的片段着色器都应该用这段代码替换
#ifdef FRAGMENT
void main()
{
vec4 textureColor =
texture2D(_MainTex, vec2(textureCoordinates));
gl_FragColor = vec4(diffuseColor * vec3(textureColor)
+ specularColor * (1.0 - textureColor.a), 1.0);
}
#endif
请注意,通常的光泽贴图需要乘以 textureColor.a
,而不是 (1.0 - textureColor.a)
。
恭喜!你完成了关于光泽映射的重要教程。我们已经了解了
- 什么是光泽映射。
- 如何为逐像素光照实现它。
- 如何为每个顶点光照实现它。
如果你还想了解更多
- 关于逐像素光照(不使用纹理),你应该阅读 “平滑镜面高光”部分.
- 关于纹理,你应该阅读 “纹理球体”部分.
- 关于使用纹理的每个顶点光照,你应该阅读 “光照纹理表面”部分.