您的位置:首页 > 移动开发 > Unity3D

Cg Programming/Unity/Glossy Textures光泽纹理

2017-11-02 19:39 381 查看

从国际空间站(ISS)看到的太平洋上空带有镜面高光的日没。

本教程涵盖了部分光滑纹理表面的逐像素光照。

译者注:通过普通的镜面高光(specular light)方程,可以使得模型在某个角度看起来具有光泽。 但是有时候我们想使得模型的高光区域是不规则的。您可以使用光泽贴图(Gloss Map)控制反射高光显示位置。指定给光泽度材质组件的贴图决定整个曲面的哪些区域更有光泽,哪些区域不太有光泽,具体情况取决于贴图中颜色的强度。贴图中的黑色像素将产生全面的光泽。白色像素将完全消除光泽,中间值会减少高光的大小。

光泽贴图是一张黑白纹理,我们使用这张纹理控制特定顶点的反射程度,黑白纹理让我们可以很容易地做到这点

扩展阅读

它结合章节“纹理球体”和“光滑镜面高光”中的着色器代码来计算带有一个材质颜色的逐像素光照,对于漫反射它是由纹理的RGB分量决定的,镜面反射的强度是由相同纹理的A分量决定的。如果你没有读过这些章节,这会是一个非常好的机会来了解它们。

光泽映射

在章节“光照纹理表面”中,漫反射的材质常量是由纹理贴图的RGB分量决定的。这里我们延伸一下这个技术,然后通过相同纹理贴图的A (alpha)分量来决定镜面反射的强度。只使用一张纹理就提供了显著的性能优势,特别是因为RGBA纹理查找代价在某种情况下跟RGB纹理查找同样昂贵(译者注:为什么昂贵可以参考以上的扩展阅读)。

如果纹理贴图的“光泽度”(即镜面反射的强度)被编码在RGBA纹理贴图的A (alpha)中,我们可以简单地把镜面反射材质常量

与纹理贴图的alpha分量进行相乘。

在章节“镜面高光”中介绍过,并且它出现在Phone反射模型的镜面反射项中:



如果乘以纹理贴图的alpha分量,这个值就会在alpha为1时达到最大(也就是表面是光泽的),在alpha为0时等于0(也就是表面没有任何光泽)。

逐像素光照代码


带有透明水的地球地图,也就是水的alpha分量为0,陆地的alpha分量为1。

下面这段着色器代码结合了章节“光滑镜面高光”的逐像素光照和章节“纹理球体”中的纹理映射。跟章节“光照纹理表面”类似,在textureColor中纹理颜色的RGB分量乘以漫射材质颜色_Color。

在上图中,水的alpha分量为0,陆地的alpha分量为1。但是,水应该是有光泽的而陆地没有。因此,在这个特定的图片中,我们应该用(1.0 - textureColor.a)乘以镜面材质颜色。另一方面,通常光泽映射会需要跟textureColor.a相乘。(注意对于着色器编程来讲这种改变有多简单。)

Shader "Cg 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

CGPROGRAM

#pragma vertex vert
#pragma fragment frag

#include "UnityCG.cginc"
uniform float4 _LightColor0;
// color of light source (from "Lighting.cginc")

// User-specified properties
uniform sampler2D _MainTex;
uniform float4 _Color;
uniform float4 _SpecColor;
uniform float _Shininess;

struct vertexInput {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};
struct vertexOutput {
float4 pos : SV_POSITION;
float4 posWorld : TEXCOORD0;
float3 normalDir : TEXCOORD1;
float4 tex : TEXCOORD2;
};

vertexOutput vert(vertexInput input)
{
vertexOutput output;

float4x4 modelMatrix = _Object2World;
float4x4 modelMatrixInverse = _World2Object;

output.posWorld = mul(modelMatrix, input.vertex);
output.normalDir = normalize(
mul(float4(input.normal, 0.0), modelMatrixInverse).xyz);
output.tex = input.texcoord;
output.pos = mul(UNITY_MATRIX_MVP, input.vertex);
return output;
}

float4 frag(vertexOutput input) : COLOR
{
float3 normalDirection = normalize(input.normalDir);

float3 viewDirection = normalize(
_WorldSpaceCameraPos - input.posWorld.xyz);
float3 lightDirection;
float attenuation;

float4 textureColor = tex2D(_MainTex, input.tex.xy);

if (0.0 == _WorldSpaceLightPos0.w) // directional light?
{
attenuation = 1.0; // no attenuation
lightDirection =
normalize(_WorldSpaceLightPos0.xyz);
}
else // point or spot light
{
float3 vertexToLightSource =
_WorldSpaceLightPos0.xyz - input.posWorld.xyz;
float distance = length(vertexToLightSource);
attenuation = 1.0 / distance; // linear attenuation
lightDirection = normalize(vertexToLightSource);
}

float3 ambientLighting = textureColor.rgb
* UNITY_LIGHTMODEL_AMBIENT.rgb * _Color.rgb;

float3 diffuseReflection = textureColor.rgb
* attenuation * _LightColor0.rgb * _Color.rgb
* max(0.0, dot(normalDirection, lightDirection));

float3 specularReflection;
if (dot(normalDirection, lightDirection) < 0.0)
// light source on the wrong side?
{
specularReflection = float3(0.0, 0.0, 0.0);
// no specular reflection
}
else // light source on the right side
{
specularReflection = attenuation * _LightColor0.rgb
* _SpecColor.rgb * (1.0 - textureColor.a)
// for usual gloss maps: "... * textureColor.a"
* pow(max(0.0, dot(
reflect(-lightDirection, normalDirection),
viewDirection)), _Shininess);
}

return float4(ambientLighting + diffuseReflection
+ specularReflection, 1.0);
}

ENDCG
}

Pass {
Tags { "LightMode" = "ForwardAdd" }
bc67

// pass for additional light sources
Blend One One // additive blending

CGPROGRAM

#pragma vertex vert
#pragma fragment frag

#include "UnityCG.cginc"
uniform float4 _LightColor0;
// color of light source (from "Lighting.cginc")

// User-specified properties
uniform sampler2D _MainTex;
uniform float4 _Color;
uniform float4 _SpecColor;
uniform float _Shininess;

struct vertexInput {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};
struct vertexOutput {
float4 pos : SV_POSITION;
float4 posWorld : TEXCOORD0;
float3 normalDir : TEXCOORD1;
float4 tex : TEXCOORD2;
};

vertexOutput vert(vertexInput input)
{
vertexOutput output;

float4x4 modelMatrix = _Object2World;
float4x4 modelMatrixInverse = _World2Object;

output.posWorld = mul(modelMatrix, input.vertex);
output.normalDir = normalize(
mul(float4(input.normal, 0.0), modelMatrixInverse).xyz);
output.tex = input.texcoord;
output.pos = mul(UNITY_MATRIX_MVP, input.vertex);
return output;
}

float4 frag(vertexOutput input) : COLOR
{
float3 normalDirection = normalize(input.normalDir);

float3 viewDirection = normalize(
_WorldSpaceCameraPos - input.posWorld.xyz);
float3 lightDirection;
float attenuation;

float4 textureColor = tex2D(_MainTex, input.tex.xy);

if (0.0 == _WorldSpaceLightPos0.w) // directional light?
{
attenuation = 1.0; // no attenuation
lightDirection =
normalize(_WorldSpaceLightPos0.xyz);
}
else // point or spot light
{
float3 vertexToLightSource =
_WorldSpaceLightPos0.xyz - input.posWorld.xyz;
float distance = length(vertexToLightSource);
attenuation = 1.0 / distance; // linear attenuation
lightDirection = normalize(vertexToLightSource);
}

float3 diffuseReflection = textureColor.rgb
* attenuation * _LightColor0.rgb * _Color.rgb
* max(0.0, dot(normalDirection, lightDirection));

float3 specularReflection;
if (dot(normalDirection, lightDirection) < 0.0)
// light source on the wrong side?
{
specularReflection = float3(0.0, 0.0, 0.0);
// no specular reflection
}
else // light source on the right side
{
specularReflection = attenuation * _LightColor0.rgb
* _SpecColor.rgb * (1.0 - textureColor.a)
// for usual gloss maps: "... * textureColor.a"
* pow(max(0.0, dot(
reflect(-lightDirection, normalDirection),
viewDirection)), _Shininess);
}

return float4(diffuseReflection
+ specularReflection, 1.0);
// no ambient lighting in this pass
}

ENDCG
}
}
Fallback "Specular"
}


对于以上特定的图像的着色器,一个有意义的修改就是在alpha分量为0时设置漫反射材质颜色为深蓝色。

逐顶点光照着色器

就像在章节“光滑镜面高光”中讨论的一样,用逐顶点光照的镜面高光通常渲染得不是很好。但是,有时因为性能上限制我们并没有其它选择。为了在章节“光照纹理表面”中的着色器包含光泽映射,两个通道的片元着色器应该被以下的代码替换:

float4 frag(vertexOutput input) : COLOR
{
float4 textureColor = tex2D(_MainTex, input.tex.xy);
return float4(input.specularColor * (1.0 - textureColor.a) +
input.diffuseColor * textureColor.rgb, 1.0);
}


注意通常的光泽映射应该需要跟textureColor.a相乘而不是(1.0 - textureColor.a)。

总结

恭喜,你完成了关于光泽映射最重要一章的学习。我们学到了:

什么是光泽映射

如何用逐顶点光照实现光泽映射

如何用逐像素光照实现光泽映射

扩展阅读

关于逐像素光照(没有纹理),你应该阅读章节“光滑镜面高光”。

关于纹理映射,你应该阅读章节“纹理球体”。

关于纹理映射的逐顶点光照,你应该阅读章节“光照纹理表面”。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: