GLSL 编程/Unity/双面表面
外观
本教程涵盖了双面逐顶点光照。
它是关于 Unity 中基本光照的一系列教程的一部分。在本教程中,我们扩展了“镜面高光”部分以渲染双面表面。如果你还没有阅读“镜面高光”部分,现在是一个很好的时机。
正如代数曲面图所示,有时将不同的颜色应用于表面的两面是有用的。在“剖视图”部分,我们已经看到了片段着色器如何使用内置变量gl_FrontFacing
来确定一个片段是属于正面三角形还是背面三角形。顶点着色器也可以确定它是否属于正面三角形或背面三角形吗?答案很明确:不能!一个原因是同一个顶点可以同时属于正面和背面三角形;因此,无论在顶点着色器中做出什么决定,它对于某些三角形都有可能是错误的。如果你想记住一个简单的规则:“片段要么是正面,要么是背面。顶点是双面的。”
因此,双面逐顶点光照必须让片段着色器来确定应该应用正面材质颜色还是背面材质颜色。例如,使用此片段着色器
#ifdef FRAGMENT
varying vec4 frontColor; // color for front face
varying vec4 backColor; // color for back face
void main()
{
if (gl_FrontFacing) // is the fragment part of a front face?
{
gl_FragColor = frontColor;
}
else // fragment is part of a back face
{
gl_FragColor = backColor;
}
}
#endif
另一方面,这意味着顶点着色器必须计算两次表面光照:一次用于正面,一次用于背面。幸运的是,这通常仍然比为每个片段计算表面光照工作量少。
双面逐顶点光照的着色器代码是对“镜面高光”部分中代码的简单扩展。它需要两组材质参数(正面和背面)并停用剔除。顶点着色器计算两种颜色,一种用于正面,一种用于背面,使用取反的法线向量和第二组材质参数。然后片段着色器决定应用哪种颜色。
Shader "GLSL two-sided per-vertex lighting" {
Properties {
_Color ("Front Material Diffuse Color", Color) = (1,1,1,1)
_SpecColor ("Front Material Specular Color", Color) = (1,1,1,1)
_Shininess ("Front Material Shininess", Float) = 10
_BackColor ("Back Material Diffuse Color", Color) = (1,1,1,1)
_BackSpecColor ("Back Material Specular Color", Color)
= (1,1,1,1)
_BackShininess ("Back Material Shininess", Float) = 10
}
SubShader {
Pass {
Tags { "LightMode" = "ForwardBase" }
// pass for ambient light and first light source
Cull Off // render front faces and back faces
GLSLPROGRAM
// User-specified properties
uniform vec4 _Color;
uniform vec4 _SpecColor;
uniform float _Shininess;
uniform vec4 _BackColor;
uniform vec4 _BackSpecColor;
uniform float _BackShininess;
// 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 frontColor;
// lighting of front faces computed in the vertex shader
varying vec4 backColor;
// lighting of back faces computed in the vertex shader
#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 viewDirection = normalize(vec3(
vec4(_WorldSpaceCameraPos, 1.0)
- modelMatrix * gl_Vertex));
vec3 lightDirection;
float attenuation;
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
- modelMatrix * gl_Vertex);
float distance = length(vertexToLightSource);
attenuation = 1.0 / distance; // linear attenuation
lightDirection = normalize(vertexToLightSource);
}
// Computation of lighting for front faces
vec3 ambientLighting =
vec3(gl_LightModel.ambient) * vec3(_Color);
vec3 diffuseReflection =
attenuation * vec3(_LightColor0) * vec3(_Color)
* 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) * pow(max(0.0, dot(
reflect(-lightDirection, normalDirection),
viewDirection)), _Shininess);
}
frontColor = vec4(ambientLighting + diffuseReflection
+ specularReflection, 1.0);
// Computation of lighting for back faces
// (uses negative normalDirection and back material colors)
vec3 backAmbientLighting =
vec3(gl_LightModel.ambient) * vec3(_BackColor);
vec3 backDiffuseReflection =
attenuation * vec3(_LightColor0) * vec3(_BackColor)
* max(0.0, dot(-normalDirection, lightDirection));
vec3 backSpecularReflection;
if (dot(-normalDirection, lightDirection) < 0.0)
// light source on the wrong side?
{
backSpecularReflection = vec3(0.0, 0.0, 0.0);
// no specular reflection
}
else // light source on the right side
{
backSpecularReflection =
attenuation * vec3(_LightColor0)
* vec3(_BackSpecColor) * pow(max(0.0, dot(
reflect(-lightDirection, -normalDirection),
viewDirection)), _BackShininess);
}
backColor = vec4(backAmbientLighting +
backDiffuseReflection + backSpecularReflection, 1.0);
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
#endif
#ifdef FRAGMENT
void main()
{
if (gl_FrontFacing)
// is the fragment part of a front face?
{
gl_FragColor = frontColor;
}
else // fragment is part of a back face
{
gl_FragColor = backColor;
}
}
#endif
ENDGLSL
}
Pass {
Tags { "LightMode" = "ForwardAdd" }
// pass for additional light sources
Blend One One // additive blending
Cull Off // render front faces and back faces
GLSLPROGRAM
// User-specified properties
uniform vec4 _Color;
uniform vec4 _SpecColor;
uniform float _Shininess;
uniform vec4 _BackColor;
uniform vec4 _BackSpecColor;
uniform float _BackShininess;
// 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 frontColor;
// lighting of front faces computed in the vertex shader
varying vec4 backColor;
// lighting of back faces computed in the vertex shader
#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 viewDirection = normalize(vec3(
vec4(_WorldSpaceCameraPos, 1.0)
- modelMatrix * gl_Vertex));
vec3 lightDirection;
float attenuation;
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
- modelMatrix * gl_Vertex);
float distance = length(vertexToLightSource);
attenuation = 1.0 / distance; // linear attenuation
lightDirection = normalize(vertexToLightSource);
}
// Computation of lighting for front faces
vec3 diffuseReflection =
attenuation * vec3(_LightColor0) * vec3(_Color)
* 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) * pow(max(0.0, dot(
reflect(-lightDirection, normalDirection),
viewDirection)), _Shininess);
}
frontColor = vec4(diffuseReflection
+ specularReflection, 1.0);
// Computation of lighting for back faces
// (uses negative normalDirection and back material colors)
vec3 backDiffuseReflection =
attenuation * vec3(_LightColor0) * vec3(_BackColor)
* max(0.0, dot(-normalDirection, lightDirection));
vec3 backSpecularReflection;
if (dot(-normalDirection, lightDirection) < 0.0)
// light source on the wrong side?
{
backSpecularReflection = vec3(0.0, 0.0, 0.0);
// no specular reflection
}
else // light source on the right side
{
backSpecularReflection =
attenuation * vec3(_LightColor0)
* vec3(_BackSpecColor) * pow(max(0.0, dot(
reflect(-lightDirection, -normalDirection),
viewDirection)), _BackShininess);
}
backColor = vec4(backDiffuseReflection
+ backSpecularReflection, 1.0);
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
#endif
#ifdef FRAGMENT
void main()
{
if (gl_FrontFacing)
// is the fragment part of a front face?
{
gl_FragColor = frontColor;
}
else // fragment is part of a back face
{
gl_FragColor = backColor;
}
}
#endif
ENDGLSL
}
}
// The definition of a fallback shader should be commented out
// during development:
// Fallback "Specular"
}
同样,此代码由两段组成,第二段与第一段相同,除了加性混合和缺少的环境色。
恭喜你完成了这个包含一个长着色器的简短教程。我们已经看到了
- 为什么顶点着色器无法区分正面和背面顶点(因为同一个顶点可能属于正面和背面三角形)。
- 如何在顶点着色器中计算正面和背面的光照。
- 如何让片段着色器决定应用哪种颜色。
如果你还想了解更多
除非另有说明,本页面上的所有示例源代码均授予公有领域。