当前位置:   article > 正文

【Unity3D】Shader常量、变量、结构体、函数_unity shader中#define

unity shader中#define

1 源码路径

        Unity Shader 常量、变量、结构体、函数一般可以在 Unity Editor 安装目录下面的【Editor\Data\CGIncludes\UnityShader】目录下查看源码,主要源码文件如下:

  • UnityCG.cginc
  • UnityShaderUtilities.cginc
  • UnityShaderVariables.cginc

2 Shader 常量

  1. #define UNITY_PI 3.14159265359f
  2. #define UNITY_TWO_PI 6.28318530718f
  3. #define UNITY_FOUR_PI 12.56637061436f
  4. #define UNITY_INV_PI 0.31830988618f
  5. #define UNITY_INV_TWO_PI 0.15915494309f
  6. #define UNITY_INV_FOUR_PI 0.07957747155f
  7. #define UNITY_HALF_PI 1.57079632679f
  8. #define UNITY_INV_HALF_PI 0.636619772367f

3 Shader 变量

        1)时间变量

  1. // Time (t = time since current level load) values from Unity
  2. float4 _Time; // (t/20, t, t*2, t*3)
  3. float4 _SinTime; // sin(t/8), sin(t/4), sin(t/2), sin(t)
  4. float4 _CosTime; // cos(t/8), cos(t/4), cos(t/2), cos(t)
  5. float4 unity_DeltaTime; // dt, 1/dt, smoothdt, 1/smoothdt

        2)相机和光源的世界坐标

  1. float3 _WorldSpaceCameraPos; // 相机的世界坐标
  2. half4 _WorldSpaceLightPos0; // 光源的世界坐标(当光源是平行光时, _WorldSpaceLightPos0表示灯光照射方向)

        3)远近裁剪平面参数

  1. // x = 1 or -1 (-1 if projection is flipped)
  2. // y = near plane
  3. // z = far plane
  4. // w = 1/far plane
  5. float4 _ProjectionParams;
  6. // 用于线性化深度, 如: LinearEyeDepth、Linear01Depth函数内部会调用
  7. // x = 1-far/near
  8. // y = far/near
  9. // z = (near-far)/(near*far)
  10. // w = 1/near
  11. // 用于 reversed depth buffer (UNITY_REVERSED_Z is 1)
  12. // x = -1+far/near
  13. // y = 1
  14. // z = (far-near)/(near*far)
  15. // w = 1/far
  16. float4 _ZBufferParams;

        4)屏幕参数

  1. // x = width
  2. // y = height
  3. // z = 1 + 1.0/width
  4. // w = 1 + 1.0/height
  5. float4 _ScreenParams;

        5)纹理参数

  1. // _MainTex的像素尺寸大小, float4(1/width, 1/height, width, height)
  2. float4 _MainTex_TexelSize;
  3. // _MainTex缩放和偏移
  4. float4 _MainTex_ST;

        补充:UV 空间是左手坐标系,U 轴从左到右,V 轴从下到上。 

        6)MVP 矩阵

  1. float4x4 UNITY_MATRIX_M, unity_ObjectToWorld; // [模型空间->世界空间]的变换矩阵M
  2. float4x4 UNITY_MATRIX_V, unity_MatrixV; // [世界空间->观察空间]的变换矩阵V
  3. float4x4 UNITY_MATRIX_P, glstate_matrix_projection; // [观察空间->裁剪空间]的变换矩阵P
  4. float4x4 UNITY_MATRIX_MV, unity_MatrixMV; // [模型空间->观察空间]的变换矩阵MV
  5. float4x4 UNITY_MATRIX_VP, unity_MatrixVP; // [世界空间->裁剪空间]的变换矩阵VP
  6. float4x4 UNITY_MATRIX_MVP, unity_MatrixMVP; // [模型空间->裁剪空间]的变换矩阵MVP
  7. float4x4 UNITY_MATRIX_I_V, unity_MatrixInvV; // V矩阵的逆矩阵
  8. float4x4 UNITY_MATRIX_T_MV, unity_MatrixTMV; // MV矩阵的转置
  9. float4x4 UNITY_MATRIX_IT_MV, unity_MatrixITMV; // MV矩阵的逆转矩阵
  10. float4x4 unity_WorldToObject; // [世界空间->模型空间]的变换矩阵M

        说明:unity_ObjectToWorld 与 unity_WorldToObject 互为逆矩阵。

4 Shader 结构体

        1)appdata_base

  1. struct appdata_base {
  2. float4 vertex : POSITION; // 模型坐标系下顶点坐标
  3. float3 normal : NORMAL; // 模型坐标系下法线向量
  4. float4 texcoord : TEXCOORD0; // 纹理坐标
  5. UNITY_VERTEX_INPUT_INSTANCE_ID
  6. };

        2)appdata_tan

  1. struct appdata_tan {
  2. float4 vertex : POSITION; // 模型坐标系下顶点坐标
  3. float4 tangent : TANGENT; // 模型坐标系下切线向量
  4. float3 normal : NORMAL; // 模型坐标系下法线向量
  5. float4 texcoord : TEXCOORD0; // 纹理坐标
  6. UNITY_VERTEX_INPUT_INSTANCE_ID
  7. };

        3)appdata_full

  1. struct appdata_full {
  2. float4 vertex : POSITION; // 模型坐标系下顶点坐标
  3. float4 tangent : TANGENT; // 模型坐标系下切线向量
  4. float3 normal : NORMAL; // 模型坐标系下法线向量
  5. float4 texcoord : TEXCOORD0; // 纹理坐标0
  6. float4 texcoord1 : TEXCOORD1; // 纹理坐标1
  7. float4 texcoord2 : TEXCOORD2; // 纹理坐标2
  8. float4 texcoord3 : TEXCOORD3; // 纹理坐标3
  9. fixed4 color : COLOR; // 顶点颜色
  10. UNITY_VERTEX_INPUT_INSTANCE_ID
  11. };

        4)appdata_img

  1. struct appdata_img
  2. {
  3. float4 vertex : POSITION; // 模型坐标系下顶点坐标
  4. half2 texcoord : TEXCOORD0; // 纹理坐标
  5. UNITY_VERTEX_INPUT_INSTANCE_ID
  6. };

        5)v2f_img

  1. struct v2f_img
  2. {
  3. // 作为顶点着色器输出时, pos指裁剪坐标系下的坐标; 作为片元着色器输入时, pos指屏幕坐标系下的坐标
  4. float4 pos : SV_POSITION;
  5. half2 uv : TEXCOORD0; // 纹理坐标
  6. UNITY_VERTEX_INPUT_INSTANCE_ID
  7. UNITY_VERTEX_OUTPUT_STEREO
  8. };

        说明:作为顶点着色器输出时, pos指裁剪坐标系下的坐标; 作为片元着色器输入时, pos指屏幕坐标系下的坐标。

        6)vert_img 着色器

  1. v2f_img vert_img(appdata_img v)
  2. {
  3. v2f_img o;
  4. UNITY_INITIALIZE_OUTPUT(v2f_img, o);
  5. UNITY_SETUP_INSTANCE_ID(v);
  6. UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
  7. o.pos = UnityObjectToClipPos(v.vertex);
  8. o.uv = v.texcoord;
  9. return o;
  10. }

        用户可以通过以下方式引用内置顶点着色器:

#pragma vertex vert_img

5 Shader 函数

5.1 基础函数

        1)数值计算 

  1. sign(x)、abs(x) // 符号、绝对值
  2. min(a, b)、max(a, b) // 最值函数
  3. ceil(x)、floor(x)、round(x) // 取整函数
  4. frac(x) // 取小数部分
  5. fmod(x, y) // 取余数
  6. rap(x) // 倒数(1/x)
  7. sqrt(x)、pow(x) // 幂函数
  8. exp(x)、exp2(x) // 指数函数(e^x、2^x)
  9. log(x)、log10(x)、log2(x) // 对数函数
  10. degrees(x)、radians(x) // 角度转换函数
  11. sin(x)、cos(x)、tan(x)、asin(x)、acos(x)、atan(x) // 三角函数
  12. sinh(x)、cosh(x)、tanh(x) // 双曲线函数
  13. saturate(x) // 将x约束在0和1之间, 超过边界就取边界值
  14. clamp(x, min, max) // 将x约束在min和max之间, 超过边界就取边界值
  15. smoothstep(min, max, x) // 平滑比例, 公式: k=saturate((x-min)/(max-min)), y=k*k*(3-2*k)
  16. mix(a, b, f) // 混合, 公式: y=(1-f)*a+f*b
  17. lerp(a, b, f) // 插值, 公式: y=(1-f)*a+f*b, a、b可以是向量
  18. step(a, b) // 如果a>b, 返回0; 如果a<=b, 返回1; 当a、b是向量时, 每个分量独立判断, 如: step(fixed2(1,1),fixed(0,2))=(0,1)

        2)向量计算

  1. all(vec) // 如果vec中每个分量都是非零的则返回true, 否则返回false
  2. any(vec) // 如果vec中存在一个分量是非零的则返回true, 否则返回false
  3. distance(pos1, pos2) // 计算pos1与pos2之间的距离
  4. length(vec) // 计算向量的模长
  5. normalize(vec) // 计算向量的单位向量
  6. dot(vec1, vec2) // 向量点乘
  7. cross(vec1, vec2) // 向量叉乘
  8. reflect(i, n) // 根据入射向量和法线向量, 计算反射向量(i和n不需要归一化)
  9. refract(i, n, ratio); // 根据入射向量、法线向量、折射率比值, 计算折射向量(i和n需要归一化, ratio为入射介质折射率/折射介质折射率, 或sin(折射角)/sin(入射角))

        3)矩阵计算

  1. // mul(M, v) = mul(v, transpose(M)), mul(v, M) = mul(transpose(M), v)
  2. mul(M, N)、mul(M, v), mul(v, M)
  3. determinant(M) // 计算矩阵的行列式
  4. transpose(M) // 矩阵转置

        补充:CG 中矩阵类型变量初始化顺序是按照行优先的顺序进行的,访问矩阵中元素也是按照行优先的顺序进行的;float3、float4 类型变量,既可以当作一个矢量,也可以当作一个 1 x n 的行矩阵或 n x 1 的列矩阵。

  1. // 定义一个矩阵, 第一行: 1.0, 2.0, 3.0; 第二行: 4.0, 5.0, 6.0; 第三行: 7.0, 8.0, 9.0
  2. float3x3 M = float3x3(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0);
  3. // 得到矩阵M的第一行, 即: 1.0, 2.0, 3.0
  4. float3 row = M[0];
  5. // 得到矩阵M的第2行、第3列元素, 即: 6.0
  6. float e = M[1][2];

        4)纹理计算

  1. tex2D(sampler2D, uv_Tex) // 查询纹理坐标对应的纹理值
  2. texCUBE(cubemap, worldVec) // 查询向量方向对应的立方体纹理值
  3. UnpackNormal(color) // 根据法线纹理解析法线向量(切线空间)

5.2 坐标和向量变换

        1)坐标变换

  1. // 模型空间->观察空间
  2. float3 UnityObjectToViewPos(float3 pos) // mul(UNITY_MATRIX_MV, float4(pos, 1.0)).xyz
  3. float3 UnityObjectToViewPos(float4 pos) // UnityObjectToViewPos(pos.xyz)
  4. // 模型空间->裁剪空间
  5. float4 UnityObjectToClipPos(float3 pos) // mul(UNITY_MATRIX_MVP, float4(pos, 1.0))
  6. float4 UnityObjectToClipPos(float4 pos) // UnityObjectToClipPos(pos.xyz)
  7. // 世界空间->观察空间
  8. float3 UnityWorldToViewPos(float3 pos) // mul(UNITY_MATRIX_V, float4(pos, 1.0)).xyz
  9. // 世界空间->裁剪空间
  10. float4 UnityWorldToClipPos(float3 pos) // mul(UNITY_MATRIX_VP, float4(pos, 1.0))
  11. // 观察空间->裁剪空间
  12. float4 UnityViewToClipPos(float3 pos) // mul(UNITY_MATRIX_P, float4(pos, 1.0))

        2)屏幕空间变换

  1. // 裁剪空间->屏幕空间(输入: 裁剪空间坐标, 输出: 屏幕空间坐标)
  2. // o.xy = 0.5 * (pos.xy + pos.w), o.zw = pos.zw
  3. float4 ComputeScreenPos(float4 pos)
  4. float4 ComputeGrabScreenPos(float4 pos)

         说明:ComputeGrabScreenPos 和 ComputeScreenPos 函数实现基本差不多,主要差异是针对平台差异造成的采样坐标问题进行了处理,如果使用了 GrabPass 抓取屏幕图像,建议使用 ComputeGrabScreenPos 函数进行屏幕纹理坐标计算;假设 ComputeScreenPos 和 ComputeGrabScreenPos 函数的输出值为 o,则 o / o.w 才是归一化(值域为 [0, 1])的屏幕坐标,通常在顶点着色器中调用 ComputeGrabScreenPos 或 ComputeScreenPos 函数,在片元着色器中执行 o / o.w,如果在顶点着色器中先执行 o / o.w(非线性变换),就不能保证光栅化插值后的坐标与输入的 pos 坐标保持线性性质,造成采样纹理扭曲问题。

        3)向量变换

  1. // 模型空间->世界空间
  2. float3 UnityObjectToWorldDir(float3 dir) // normalize(mul((float3x3)unity_ObjectToWorld, dir))
  3. // 世界空间->模型空间
  4. float3 UnityWorldToObjectDir(float3 dir) // normalize(mul((float3x3)unity_WorldToObject, dir))

        4)法线变换

  1. // 模型空间->世界空间(已归一化)
  2. float3 UnityObjectToWorldNormal(float3 norm) {
  3. #ifdef UNITY_ASSUME_UNIFORM_SCALING // 统一缩放(x、y、z分量缩放系数一致)
  4. return UnityObjectToWorldDir(norm); // normalize(mul((float3x3)unity_ObjectToWorld, norm))
  5. #else
  6. return normalize(mul(norm, (float3x3)unity_WorldToObject)); // mul(IT_M, norm) => mul(norm, I_M)
  7. #endif
  8. }

        法线由切线计算而来,在模型空间中 A 点的切线向量为 v1,法线向量为 n1,经过模型变换(矩阵 M)后,切线向量为 v2,法线向量为 n2,假设法线向量的变换矩阵为 G,因此存在以下关系:

        Unity 中线性变换主要有平移、旋转、缩放,由于向量不受平移变换影响,因此,对于法线向量而言,只受旋转和缩放影响。

  • 当 M 只包含旋转变换时,M 是正交矩阵,M-1=MT,因此 G = M;
  • 当 M 只包含统一缩放变换时,M = k·E,因此 G = 1/k·E = 1/(k^2)·M,由于法线向量只需要方向,后面会进行归一化,因此可以简写 G = M;
  • 当 M 只包含旋转变换和统一缩放变换时,G = 1/(k^2)·M,由于法线向量只需要方向,后面会进行归一化,因此可以简写 G = M;

        5)其他变换

  1. // 观察空间->裁剪空间
  2. float2 TransformViewToProjection (float2 v) // mul((float2x2)UNITY_MATRIX_P, v)
  3. float3 TransformViewToProjection (float3 v) // mul((float3x3)UNITY_MATRIX_P, v)

5.3 计算指向相机和光源的向量 

        1)计算顶点指向相机的向量

  1. // _WorldSpaceCameraPos.xyz - worldPos
  2. float3 ObjSpaceViewDir(float4 v) // 输入: 模型空间坐标, 输出: 模型空间坐标
  3. float3 WorldSpaceViewDir(float4 localPos) // 输入: 模型空间坐标, 输出: 世界空间坐标
  4. float3 UnityWorldSpaceViewDir(float3 worldPos) // 输入: 世界空间坐标, 输出: 世界空间坐标

        2)计算顶点指向光源的向量

  1. // mul(unity_WorldToObject, _WorldSpaceLightPos0).xyz - v.xyz
  2. float3 ObjSpaceLightDir(float4 v) // 输入: 模型空间坐标, 输出: 模型空间坐标
  3. float3 WorldSpaceLightDir(float4 localPos) // 输入: 模型空间坐标, 输出: 世界空间坐标
  4. float3 UnityWorldSpaceLightDir(float3 worldPos) // 输入: 世界空间坐标, 输出: 世界空间坐标

5.4 深度纹理和法线纹理相关函数

        1)深度纹理采样

  1. // 非线性的深度(即计算的深度值与实际深度值不是线性关系)
  2. float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv); // tex2D(_CameraDepthTexture, i.uv).r
  3. // 观察空间中的线性的深度, 值域: [Near, Far], 公式: 1.0 / (_ZBufferParams.z * depth + _ZBufferParams.w)
  4. float linearDepth = LinearEyeDepth(depth);
  5. // 观察空间中的线性且归一化的深度, 值域: [0, 1], 公式: 1.0 / (_ZBufferParams.x * depth + _ZBufferParams.y)
  6. float linear01Depth = Linear01Depth(depth);

        LinearEyeDepth 和 Linear01Depth 源码分析见→屏幕深度和法线纹理简介

        2)深度&法线纹理采样

  1. fixed4 tex = tex2D(_CameraDepthNormalsTexture, i.uv);
  2. float depth = DecodeFloatRG(tex.zw); // 观察空间中的线性且归一化的深度
  3. float3 normal = DecodeViewNormalStereo(tex); // 观察空间中的法线向量

        也可以使用 DecodeDepthNormal 函数获取深度和法线信息,其实现如下:

  1. inline void DecodeDepthNormal(float4 enc, out float depth, out float3 normal)
  2. { // 深度&法线采样, enc为tex2D采样结果, depth、normal为解码后的深度和法线
  3. depth = DecodeFloatRG(enc.zw); // 观察空间中的线性且归一化的深度
  4. normal = DecodeViewNormalStereo(enc); // 观察空间中的法线向量
  5. }
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/108227
推荐阅读
相关标签
  

闽ICP备14008679号