11.表面着色器-结构体-输入结构体
11.1 知识点
表面着色器中的输入结构体指什么
我们上节课主要学习了编译指令
#pragma surface 表面函数名 光照模型 可选额外参数
其中在讲解表面函数名时提到,需要按规则声明函数。函数的参数有三种固定格式:
- void 表面函数名(Input IN, inout SurfaceOutput o)
- void 表面函数名(Input IN, inout SurfaceOutputStandard o)
- void 表面函数名(Input IN, inout SurfaceOutputStandardSpecular o)
而其中的这个 Input 结构体,就是我们的输入结构体。它是由我们自己声明的,是数据的来源,我们需要用到这些数据进行逻辑处理。
输入结构体中我们能声明哪些成员
Input 结构体虽然是需要我们自己声明的结构体,但只要在其中按照规定声明成员变量,便能获取到指定的表面属性。注意:
- 如果在可选额外参数中自定义了顶点修改函数,该结构体还会是顶点修改函数的输出结构体
- 除了规范好的参数,我们还可以自己添加自定义参数,比如在自定义顶点修改函数中进行赋值
- 在顶点/片元着色器中,这些参数往往需要手动计算,而在表面着色器中,可以理解为 Unity 内部已经帮助我们计算好了,直接拿来用即可
struct Input
{
// 1. uv 相关
// 规则:uv 纹理变量名,若存在次纹理坐标,也可以用 uv2 作为前缀
float2 uv_MainTex;
float2 uv_BumpMap;
// 2. 视角方向(可以用于处理边缘光照等)
float3 viewDir;
// 3. 屏幕空间坐标(可以用于处理反射或屏幕特效等)
float4 screenPos;
// 4. 世界空间下的位置
float3 worldPos;
// 5. 世界空间下的反射方向
// 没有修改 o.Normal 时;如果修改了表面法线 o.Normal,在表面函数中需要使用 WorldReflectionVector(IN, o.Normal) 来得到世界空间下的反射方向
float3 worldRefl;
// 6. 世界空间下的法线方向
// 没有修改 o.Normal 时;如果修改了表面法线 o.Normal,在表面函数中需要使用 WorldNormalVector(IN, o.Normal) 来得到世界空间下的法线方向
float3 worldNormal;
// 7. 使用 COLOR 语义定义的 float4 变量,表示插值后的逐顶点颜色
float4 vertexColor : COLOR;
};
表面函数示例
以下是表面函数的三种固定格式之一的示例,实现了对输入结构体中数据的使用:
// 1. void 表面函数名(Input IN, inout SurfaceOutput o)
// 2. void 表面函数名(Input IN, inout SurfaceOutputStandard o)
// 3. void 表面函数名(Input IN, inout SurfaceOutputStandardSpecular o)
void surf (Input IN, inout SurfaceOutput o)
{
// 我们在 Input 结构体中声明了 uv_MainTex,就可以直接使用
// 只要满足命名规则,如视角方向、世界空间下的位置等也可以直接使用
// Unity 已经帮我们转换好了
fixed4 tex = tex2D(_MainTex, IN.uv_MainTex);
o.Albedo = tex.rgb * _Color;
o.Alpha = tex.a * _Color.a;
// 乘以凹凸程度的系数
float3 tangentNormal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
tangentNormal.xy *= _BumpScale;
tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));
o.Normal = tangentNormal;
}
11.2 知识点代码
Lesson11_Shader.shader
Shader "Custom/Lesson11_Shader"
{
Properties
{
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_BumpMap("BumpMap", 2D) = "white"{}
_BumpScale("BumpScale", Range(0,1)) = 1
}
SubShader
{
Tags { "RenderType"="Opaque" }
CGPROGRAM
//vertex:FunVertex finalcolor:FunFinalcolor
#pragma surface surf Lambert
#pragma target 3.0
sampler2D _MainTex;
sampler2D _BumpMap;
float _BumpScale;//凹凸程度
fixed4 _Color;
struct Input
{
//1.uv相关
//规则:uv纹理变量名 若存在次纹理坐标,也可以用uv2作为前缀
float2 uv_MainTex;
float2 uv_BumpMap;
//2.视角方向(可以用于处理边缘光照等)
float3 viewDir;
//3.屏幕空间坐标(可以用于处理反射或屏幕特效等)
float4 screenPos;
//4.世界空间下的位置
float3 worldPos;
//5.世界空间下的反射方向,没有修改o.Normal时
//如果修改了表面法线 o.Normal
//在表面函数中,需要使用 WorldReflectionVector(IN, o.Normal) 来得到世界空间下的反射方向
float3 worldRefl;
//6.世界空间下的法线方向,没有修改o.Normal时
//如果修改了表面法线 o.Normal
//在表面函数中,需要使用 WorldNormalVector(IN, o.Normal) 来得到世界空间下的法线方向
float3 worldNormal;
//7.使用COLOR语义定义的float4变量,表示插值后的逐顶点颜色
float4 vertexColor:COLOR;
};
//1. void 表面函数名(Input IN, inout SurfaceOutput o)
//2. void 表面函数名(Input IN, inout SurfaceOutputStandard o)
//3. void 表面函数名(Input IN, inout SurfaceOutputStandardSpecular o)
void surf (Input IN, inout SurfaceOutput o)
{
// 我们在Input结构体中声明了uv_MainTex
// 就可以.出来直接使用
// 只要满足命名规则 像视角方向 世界空间下的位置 也可以直接使用
// Unity已经帮我们转换好了
fixed4 tex = tex2D (_MainTex, IN.uv_MainTex);
o.Albedo = tex.rgb * _Color;
o.Alpha = tex.a * _Color.a;
//乘以凹凸程度的系数
float3 tangentNormal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
tangentNormal.xy *= _BumpScale;
tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));
o.Normal = tangentNormal;
}
void FunVertex(inout appdata_full v)
{
//处理顶点逻辑
}
void FunFinalcolor(Input IN, SurfaceOutput o, inout fixed4 color)
{
//对最终处理完的颜色 再次进行额外处理
}
//不依赖视角的光照模型,比如漫反射
//half4 Lighting自定义名字 (SurfaceOutput s, half3 lightDir, half atten)
//依赖视角的光照模型,比如高光反射
//half4 Lighting自定义名字 (SurfaceOutput s, half3 lightDir, half3 viewDir, half atten)
//然后只需要在光照模型处填写 自定义名字 即可
//就会自动调用函数中的逻辑来处理光照相关逻辑了
half4 LightingMyLight (SurfaceOutput s, half3 lightDir, half3 viewDir, half atten)
{
}
ENDCG
}
FallBack "Diffuse"
}
Lesson11_表面着色器_输入结构体.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Lesson11_表面着色器_输入结构体 : MonoBehaviour
{
void Start()
{
#region 知识点一 表面着色器中的输入结构体指什么
//我们上节课主要学习了编译指令
//#pragma surface 表面函数名 光照模型 可选额外参数
//其中在讲解表面函数名时提到,需要按规则声明函数
//函数的参数有三种固定格式
//1. void 表面函数名(Input IN, inout SurfaceOutput o)
//2. void 表面函数名(Input IN, inout SurfaceOutputStandard o)
//3. void 表面函数名(Input IN, inout SurfaceOutputStandardSpecular o)
//而其中的这个Input结构体,就是我们的输入结构体
//它是由我们自己声明的,是数据的来源,我们需要用到这些数据进行逻辑处理
#endregion
#region 知识点二 输入结构体中我们能声明哪些成员
//Input结构体虽然是需要我们自己声明的结构体
//但是我们只要在其中按照规定声明成员变量,便能获取到指定的表面属性
//注意:
//1.如果我们在可选额外参数中自定义了顶点修改函数,该结构体还会是顶点修改函数的输出结构体
//2.除了规范好的参数,我们还可以自己添加自定义参数,比如在自定义顶点修改函数中进行赋值
//3.在顶点/片元着色器中,这些参数往往需要我们手动计算,而在表面着色器中你可以理解为Unity内部已经
// 帮助我们计算好了,直接拿来用即可
#endregion
}
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com