草庐IT

【Unity3D】Shader常量、变量、结构体、函数

little_fat_sheep 2023-09-26 原文

1 源码路径

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

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

2 Shader 常量

#define UNITY_PI            3.14159265359f
#define UNITY_TWO_PI        6.28318530718f
#define UNITY_FOUR_PI       12.56637061436f
#define UNITY_INV_PI        0.31830988618f
#define UNITY_INV_TWO_PI    0.15915494309f
#define UNITY_INV_FOUR_PI   0.07957747155f
#define UNITY_HALF_PI       1.57079632679f
#define UNITY_INV_HALF_PI   0.636619772367f

3 Shader 变量

        1)时间变量

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

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

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

        3)投影参数

// x = 1 or -1 (-1 if projection is flipped)
// y = near plane
// z = far plane
// w = 1/far plane
float4 _ProjectionParams;

        4)屏幕参数

// x = width
// y = height
// z = 1 + 1.0/width
// w = 1 + 1.0/height
float4 _ScreenParams;

        5)MVP 矩阵

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

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

4 Shader 结构体

        1)appdata_base

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

        2)appdata_tan

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

        3)appdata_full

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

        4)appdata_img

struct appdata_img
{
    float4 vertex : POSITION; // 模型坐标系下顶点坐标
    half2 texcoord : TEXCOORD0; // 纹理坐标
    UNITY_VERTEX_INPUT_INSTANCE_ID
};

        5)v2f_img

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

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

        6)vert_img 着色器

v2f_img vert_img(appdata_img v)
{
    v2f_img o;
    UNITY_INITIALIZE_OUTPUT(v2f_img, o);
    UNITY_SETUP_INSTANCE_ID(v);
    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
    o.pos = UnityObjectToClipPos(v.vertex);
    o.uv = v.texcoord;
    return o;
}

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

#pragma vertex vert_img

5 Shader 函数

5.1 基础函数

        1)数值计算 

sign(x)、abs(x) // 符号、绝对值
min(a, b)、max(a, b) // 最值函数
ceil(x)、floor(x)、round(x) // 取整函数
frac(x) // 取小数部分
fmod(x, y) // 取余数
rap(x) // 倒数(1/x)
sqrt(x)、pow(x) // 幂函数
exp(x)、exp2(x) // 指数函数(e^x、2^x)
log(x)、log10(x)、log2(x) // 对数函数
degrees(x)、radians(x) // 角度转换函数
sin(x)、cos(x)、tan(x)、asin(x)、acos(x)、atan(x) // 三角函数
sinh(x)、cosh(x)、tanh(x) // 双曲线函数
saturate(x) // 将x约束在0和1之间, 超过边界就取边界值
clamp(x, min, max) // 将x约束在min和max之间, 超过边界就取边界值
smoothstep (min, max, x) // 平滑比例, 公式: k=saturate((x-min)/(max-min)), y=k*k*(3-2*k)
lerp(a, b, f) // 插值, 公式: y=x+f*(y-x), a、b可以是向量

        2)向量计算

all(vec) // 如果vec中每个分量都是非零的则返回true, 否则返回false
any(vec) // 如果vec中存在一个分量是非零的则返回true, 否则返回false
distance(pos1, pos2) // 计算pos1与pos2之间的距离
length(vec) // 计算向量的模长
normalize(vec) // 计算向量的单位向量
dot(vec1, vec2) // 向量点乘
cross(vec1, vec2) // 向量叉乘
reflect(i, n) // 根据入射向量和法线向量计算反射向量

        3)矩阵计算

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

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

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

        4)纹理计算

tex2D(sampler2D, uv_Tex) // 查询纹理坐标对应的纹理值
UnpackNormal(color) // 根据法线纹理解析法线向量

5.2 坐标和向量变换

        1)坐标变换

// 模型空间->观察空间
float3 UnityObjectToViewPos(float3 pos) // mul(UNITY_MATRIX_MV, float4(pos, 1.0)).xyz
float3 UnityObjectToViewPos(float4 pos) // UnityObjectToViewPos(pos.xyz)
// 模型空间->裁剪空间
float4 UnityObjectToClipPos(float3 pos) // mul(UNITY_MATRIX_MVP, float4(pos, 1.0))
float4 UnityObjectToClipPos(float4 pos) // UnityObjectToClipPos(pos.xyz)
// 世界空间->观察空间
float3 UnityWorldToViewPos(float3 pos) // mul(UNITY_MATRIX_V, float4(pos, 1.0)).xyz
// 世界空间->裁剪空间
float4 UnityWorldToClipPos(float3 pos) // mul(UNITY_MATRIX_VP, float4(pos, 1.0))
// 观察空间->裁剪空间
float4 UnityViewToClipPos(float3 pos) // mul(UNITY_MATRIX_P, float4(pos, 1.0))

        2)向量变换

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

        3)法线变换

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

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

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

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

        4)其他变换

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

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

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

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

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

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

有关【Unity3D】Shader常量、变量、结构体、函数的更多相关文章

  1. ruby-on-rails - 未初始化的常量 Psych::Syck (NameError) - 2

    在我的gem中,我需要yaml并且在我的本地计算机上运行良好。但是在将我的gem推送到ruby​​gems.org之后,当我尝试使用我的gem时,我收到一条错误消息=>"uninitializedconstantPsych::Syck(NameError)"谁能帮我解决这个问题?附言RubyVersion=>ruby1.9.2,GemVersion=>1.6.2,Bundlerversion=>1.0.15 最佳答案 经过几个小时的研究,我发现=>“YAML使用未维护的Syck库,而Psych使用现代的LibYAML”因此,为了解决

  2. ruby-on-rails - 如何使用 instance_variable_set 正确设置实例变量? - 2

    我正在查看instance_variable_set的文档并看到给出的示例代码是这样做的:obj.instance_variable_set(:@instnc_var,"valuefortheinstancevariable")然后允许您在类的任何实例方法中以@instnc_var的形式访问该变量。我想知道为什么在@instnc_var之前需要一个冒号:。冒号有什么作用? 最佳答案 我的第一直觉是告诉你不要使用instance_variable_set除非你真的知道你用它做什么。它本质上是一种元编程工具或绕过实例变量可见性的黑客攻击

  3. ruby - 通过 ruby​​ 进程共享变量 - 2

    我正在编写一个gem,我必须在其中fork两个启动两个webrick服务器的进程。我想通过基类的类方法启动这个服务器,因为应该只有这两个服务器在运行,而不是多个。在运行时,我想调用这两个服务器上的一些方法来更改变量。我的问题是,我无法通过基类的类方法访问fork的实例变量。此外,我不能在我的基类中使用线程,因为在幕后我正在使用另一个不是线程安全的库。所以我必须将每个服务器派生到它自己的进程。我用类变量试过了,比如@@server。但是当我试图通过基类访问这个变量时,它是nil。我读到在Ruby中不可能在分支之间共享类变量,对吗?那么,还有其他解决办法吗?我考虑过使用单例,但我不确定这是

  4. ruby-on-rails - active_admin 目录中的常量警告重新声明 - 2

    我正在使用active_admin,我在Rails3应用程序的应用程序中有一个目录管理,其中包含模型和页面的声明。时不时地我也有一个类,当那个类有一个常量时,就像这样:classFooBAR="bar"end然后,我在每个必须在我的Rails应用程序中重新加载一些代码的请求中收到此警告:/Users/pupeno/helloworld/app/admin/billing.rb:12:warning:alreadyinitializedconstantBAR知道发生了什么以及如何避免这些警告吗? 最佳答案 在纯Ruby中:classA

  5. ruby - 在没有 sass 引擎的情况下使用 sass 颜色函数 - 2

    我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re

  6. ruby-on-rails - 如何在我的 Rails 应用程序 View 中打印 ruby​​ 变量的内容? - 2

    我是一个Rails初学者,但我想从我的RailsView(html.haml文件)中查看Ruby变量的内容。我试图在ruby​​中打印出变量(认为它会在终端中出现),但没有得到任何结果。有什么建议吗?我知道Rails调试器,但更喜欢使用inspect来打印我的变量。 最佳答案 您可以在View中使用puts方法将信息输出到服务器控制台。您应该能够在View中的任何位置使用Haml执行以下操作:-puts@my_variable.inspect 关于ruby-on-rails-如何在我的R

  7. ruby - RuntimeError(自动加载常量 Apps 多线程时检测到循环依赖 - 2

    我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("

  8. ruby-on-rails - 在 ruby​​ 中使用 gsub 函数替换单词 - 2

    我正在尝试用ruby​​中的gsub函数替换字符串中的某些单词,但有时效果很好,在某些情况下会出现此错误?这种格式有什么问题吗NoMethodError(undefinedmethod`gsub!'fornil:NilClass):模型.rbclassTest"replacethisID1",WAY=>"replacethisID2andID3",DELTA=>"replacethisID4"}end另一个模型.rbclassCheck 最佳答案 啊,我找到了!gsub!是一个非常奇怪的方法。首先,它替换了字符串,所以它实际上修改了

  9. ruby - 在 Ruby 中有条件地定义函数 - 2

    我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin

  10. ruby-on-rails - ActionController::RoutingError: 未初始化常量 Api::V1::ApiController - 2

    我有用于控制用户任务的Rails5API项目,我有以下错误,但并非总是针对相同的Controller和路由。ActionController::RoutingError:uninitializedconstantApi::V1::ApiController我向您描述了一些我的项目,以更详细地解释错误。应用结构路线scopemodule:'api'donamespace:v1do#=>Loginroutesscopemodule:'login'domatch'login',to:'sessions#login',as:'login',via::postend#=>Teamroutessc

随机推荐