Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

渐变纹理

一、渐变纹理的作用

通过单张纹理和凹凸纹理相关知识的学习,我们知道图片中存储的数据不仅仅可以是颜色数据,还可以是高度、法线数据。 理论上来说,图片中存储的数据我们可以自定义规则,我们可以往图片中存储任何满足我们需求的数据用于渲染。

渐变纹理就是用于控制漫反射光照结果的一种存储数据的方式它的 主要作用是让游戏中的对象具有插画卡通风格,可以保证物体的轮廓线相比之前使用的传统漫反射光照更加明显而且还能提供多种色调的变化,可以让模型更具卡通感

2024-09-07 170219.png

二、渐变纹理的原理

计算漫反射时利用半兰伯特光照模型公式的后半部分,得到一个 0~1 之间的值,将这个值作为 uv 坐标中的 uv 坐标,从渐变纹理中取出颜色于公式中的前面的部分进行颜色叠加,最终得到漫反射光照颜色。 即:漫反射的明暗不是再由 0~1 这个值决定,而是由渐变纹理中取出的颜色进行叠加达到的最终效果 (半兰伯特:漫反射颜色 = 光源颜色 * 材质的漫反射颜色 *((标准化后物体的表面的法向量 · 标准化后的光源方向向量))* 0.5 = 0.5) 2024-09-07 171221.png

三、渐变纹理的实现

1.书写思路

  1. 属性相关
    • 漫反射颜色
    • 渐变纹理
    • 高光反射颜色
    • 光泽度
  2. 结构体相关
    • 顶点着色器传入 可以使用 UnityCG.cginc 中的 appdata_base 可以包含我们需要的顶点、法线相关数据
    • 片元着色器中传入 自定义一个结构体 其中包含 裁剪空间下的顶点坐标、世界空间下的顶点坐标、世界空间下的法线坐标
  3. 顶点着色器回调函数中
    1. 顶点坐标 模型空间转裁剪空间
    2. 顶点坐标 模型空间转世界空间
    3. 法线 从模型空间转世界空间
  4. 片元着色器回调函数中
    1. 计算光的方向
    2. 计算半兰伯特光照后半部分公式值,即:((标准化后物体表面法线向量 · 标准化后光源方向向量)* 0.5 +0.5) 半兰伯特光照公式: 光源颜色 * 材质漫反射颜色 * ((标准化后物体表面法线向量 · 标准化后光源方向向量)* 0.5 +0.5)
    3. 利用该值从渐变纹理中取出对应颜色,参与漫反射颜色计算 即: 光源颜色 * 材质漫反射颜色 * 渐变纹理中取出的颜色
    4. 计算 BlinnPhong 光照模型,其中的漫反射光照颜色使用 4-3 中计算出来的颜色

2.实例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
Shader "Unlit/RampMap"
{
Properties
{
_MainColor("_MainColor",Color) = (1,1,1,1)
_RampTex("_RampTex",2D) = ""{}
_SpecularColor("SpecularColor",Color) = (1,1,1,1)
_SpecularNum("_SpecularNum",Range(0,20)) = 18
}
SubShader
{
Pass
{
Tags { "Lighting"="ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"

fixed4 _MainColor;
sampler2D _RampTex;
float4 _RampTex_ST;
fixed4 _SpecularColor;
float _SpecularNum;

struct v2f
{
//裁剪空间下的顶点坐标
float4 pos:SV_POSITION;
//世界空间下的顶点坐标
float3 worldPos:TEXCOORD0;
//世界坐标下的法线
float3 worldNormal:TEXCOORD1;
};
v2f vert (appdata_base v)
{
v2f data;
//将模型空间下的顶点转换到裁剪坐标下
data.pos = UnityObjectToClipPos(v.vertex);
//将模型空间下的顶点转换到世界空间下
data.worldPos = mul(unity_ObjectToWorld,v.vertex);
//将模型空间下的法线坐标转换到世界空间下
data.worldNormal = UnityObjectToWorldNormal(v.normal);
return data;
}

fixed4 frag (v2f i) : SV_Target
{
//光的方向
float3 lightDir = normalize(_WorldSpaceLightPos0);
//漫反射颜色 (通过渐变纹理得到的颜色来叠加)
fixed halfLambertNum = dot(normalize(i.worldNormal),lightDir)*0.5+0.5;
//漫反射颜色 = 光的颜色 * 漫反射颜色 * 渐变纹理中取出的颜色
fixed3 diffuseColor = _LightColor0.rgb * _MainColor.rgb * tex2D(_RampTex,fixed2(halfLambertNum,halfLambertNum));

//高光反射颜色
//视角方向
float3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
//半角方向
float3 halfA = normalize(viewDir + lightDir);
//高光反射计算
fixed3 specularColor = _LightColor0.rgb * _SpecularColor.rgb * pow(max(0,dot(halfA,i.worldNormal)),_SpecularNum);

//BlinnPhong
fixed3 color = UNITY_LIGHTMODEL_AMBIENT + diffuseColor + specularColor;

return fixed4(color.rgb,1);
}
ENDCG
}
}
}

3.实现效果

2024-09-08 151126.png 2024-09-08 151123.png

四、修改渐变纹理设置 避免黑点的出现

1.出现情况示例

Snipaste_2024-09-08_15-34-58.png

2.出现原因

浮点数的计算可能存在误差,出现超过 1 的值(例如 1.00001) 如果渐变纹理的 Wrap Mode 为 Repeat,会舍弃整数部分,仅保留小数部分 0.00001,这时颜色会是渐变纹理最左边的颜色

3.解决方法

渐变纹理的 Wrap Mode 的 Repeat 模式更改为 Clamp 即可

评论