[Unity Shader] 坐标变换与法线变换及Unity5新增加的内置函数
学习第六章Unity内置函数时,由于之前使用mul矩阵乘法时的顺序与书中不一致,导致使用内置函数时出现光照效果不一样,因此引出以下两个问题:
1 什么时候使用3x3矩阵,什么时候使用4x4矩阵?
2 法线变换矩阵与坐标变换矩阵不相同?
解答1:
4.9.1节书中讲述了何时使用3x3和4x4矩阵。因为4x4矩阵是比3x3矩阵多了平移变换,因此对空间坐标进行变换时,通常使用4x4矩阵。而对于切线和法线这两种空间矢量,不存在平移的情况,因此仅使用3x3矩阵即可(是否可以偷懒使用4x4矩阵?是可以的)。
解答2:
而对于法线变换,因为对坐标缩放时,有可能不是等比缩放,这会导致法向量使用坐标变换矩阵后不再垂直于表面。有下述推导过程求得法线变换矩阵:
假设某平面点的模型空间切线为T,法线为N。通过空间转换M,法线转换G后,切线为T',法线为N'。那么有:
TT·N = 0 ----- (1),
(T')T·N' = 0 -----(2),
T' = M·T -----(3),
N' = G·N -----(4)。
由式(2)(3)(4)有:
(M·T)T·(G·N) = 0 ==> TT·MT·G·N = 0 ==> (矩阵结合律) (TT·MT·G)·N = 0 -----(5)
结合式(1)和(5),得到:TT = TT·MT·G,由此得到 MT·G = I,则:
G = (MT)-1 = (M-1)T
即法线的变换矩阵,是空间变换矩阵的转置的逆。
======================Unity Shader中计算注意事项=======================
1 在Unity Shader中,将坐标从模型空间转换到世界空间,使用如下方式:
v2f o;
o.worldPos = mul(_Object2World, v.vertex).xyz;
UnityShader中的转换矩阵为行矩阵,模型空间坐标v.vertex作为列矩阵,与转换矩阵右乘,得到世界坐标。
注意,这里不能使用 o.worldPos = UnityObjectToWorldDir(v.vertex); 在UnityCG.cginc中查看UnityObjectToWorldDir的定义:
// Transforms direction from object to world space
inline float3 UnityObjectToWorldDir( in float3 dir )
{
return normalize(mul((float3x3)_Object2World, dir));
}
这里使用的是3x3矩阵,使得坐标变换丢失了平移参数。
2 将法线从模型空间转换到世界空间,使用如下方式:
o.worldNormal = mul(v.normal, (float3x3)_World2Object);
在这里,矩阵_World2Object是矩阵_Object2World的逆矩阵。由于是法向量,因此取3x3矩阵进行计算。
需要注意的是,这里的mul实现的矩阵乘法,当向量在左侧时,此向量相当于行向量,其中数值是与矩阵的列元素进行乘法与加法。相当于如下写法:
o.worldNormal = mul(transpose((float3x3)_World2Object), v.normal);
即法线的变换是空间变换_Object2World的逆矩阵的转置transpose(_World2Object)。
这里Unity Shader封装了一个函数来转换法线到世界坐标UnityObjectToWorldNormal(),替代上面的矩阵乘法写法,不容易出错。
o.worldNormal = UnityObjectToWorldNormal(v.normal);
// Transforms normal from object to world space
inline float3 UnityObjectToWorldNormal( in float3 norm )
{
// Multiply by transposed inverse matrix, actually using transpose() generates badly optimized code
return normalize(_World2Object[].xyz * norm.x + _World2Object[].xyz * norm.y + _World2Object[].xyz * norm.z);
}
================书中完整示例===============
Shader "Unity Shaders Book/Chapter 6/Blinn-Phong Build-In Function"
{
Properties
{
_Diffuse("Diffuse", Color) = (1.0, 1.0, 1.0, 1.0)
_Specular("Specular", Color) = (1.0, 1.0, 1.0, 1.0)
_Gloss("Gloss", Range(8.0, 256.0)) = 20.0
}
SubShader
{
Pass
{
Tags { "LightMode"="ForwardBase" } CGPROGRAM #pragma vertex vert
#pragma fragment frag #include "Lighting.cginc" fixed4 _Diffuse;
fixed4 _Specular;
float _Gloss; struct a2v
{
float4 vertex : POSITION;
float3 normal : NORMAL;
}; struct v2f
{
float4 pos : SV_POSITION;
fixed3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
}; v2f vert (a2v v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal); // 效果与下一行相同,因为是矢量变换,用3x3变换矩阵即可。
// o.worldNormal = mul(v.normal, (float3x3)_World2Object);
// o.worldPos = UnityObjectToWorldDir(v.vertex); // normalize(mul((float3x3)_Object2World, dir));效果与下一行不同
// o.worldPos = mul(v.vertex, transpose(_Object2World)).xyz; // 将坐标左乘,将模型空间坐标转换到世界空间,与下式相等
o.worldPos = mul(_Object2World, v.vertex).xyz; // 将坐标右乘,将模型空间坐标转换到世界空间
return o;
} fixed4 frag(v2f i) : SV_Target
{
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
fixed3 halfDir = normalize(viewDir + worldLightDir);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(, dot(halfDir, worldNormal)) , _Gloss);
fixed3 color = ambient + diffuse + specular;
return fixed4(color, 1.0);
} ENDCG
}
} Fallback "Specular"
}
使用Unity5 build-in的函数实现Blinn-Phong光照模型。
[Unity Shader] 坐标变换与法线变换及Unity5新增加的内置函数的更多相关文章
- Unity3D -- shader语法内置函数
该篇是Unity Shader中HLSL的内置函数,主要是一些数学方面的计算函数.在写Shader的时候可以直接使用. abs //计算输入值的绝对值. acos //返回输入值反余弦值. all / ...
- Unity3D Shader 内置函数
Intrinsic Functions (DirectX HLSL) The following table lists the intrinsic functions available in HL ...
- Unity Shader入门精要学习笔记 - 第6章 开始 Unity 中的基础光照
转自冯乐乐的<Unity Shader入门精要> 通常来讲,我们要模拟真实的光照环境来生成一张图像,需要考虑3种物理现象. 首先,光线从光源中被发射出来. 然后,光线和场景中的一些物体相交 ...
- 【Unity Shader学习笔记】Unity基础纹理-法线贴图
1 高度纹理 使用一张纹理改变物体表面法线,为模型提供更多细节. 有两种主要方法: 1.高度映射:使用一张高度纹理(height map)来模拟表面位移(displacement).得到一个修改后的法 ...
- 【Unity Shader】(四) ------ 纹理之法线纹理、单张纹理及遮罩纹理的实现
笔者使用的是 Unity 2018.2.0f2 + VS2017,建议读者使用与 Unity 2018 相近的版本,避免一些因为版本不一致而出现的问题. [Unity Shader](三) ----- ...
- 第四章 开始Unity Shader学习之旅(2)
目录 1. 强大的援手:Unity提供的内置文件和变量 1.1 内置的包含文件 1.2 内置的变量 2. Unity提供的Cg/HLSL语义 2.1 什么是语义 2.2 Unity支持的语义 2.3 ...
- Unity Shader 入门精要学习 (冯乐乐 著)
第1篇 基础篇 第1章 欢迎来到Shader的世界 第2章 渲染流水线 第3章 Unity Shader 基础 第4章 学习Shader所需的数学基础 第2篇 初级篇 第5章 开始Unity Shad ...
- Unity Shader入门精要学习笔记 - 第9章 更复杂的光照
转载自 冯乐乐的<Unity Shader入门精要> Unity 的渲染路径 在Unity里,渲染路径决定了光照是如何应该到Unity Shader 中的.因此,如果要和光源打交道,我们需 ...
- Unity Shader入门精要学习笔记 - 第5章 开始 Unity Shader 学习之旅
一个顶点/片元 着色器的结构大概如下: Shader "MyShaderName" { Properties { //属性 } SubShader { //针对显卡A的SubSha ...
随机推荐
- redis 配置文件redis.conf
daemonize yes #---默认值no,该参数用于定制redis服务是否以守护模式运行.--- pidfile /var/run/redis.pid #默认值/var/run/redis.pi ...
- python第八课——random模块的使用
2.2.如何获取随机整数值? 引入random模块的使用 randint(a,b)函数:作用:返回给程序一个[a,b]范围内的随机整数注意:含头含尾闭区间 思路步骤: 第一步:导入random模块到相 ...
- __autoreleasing 与 局部变量
__autoreleasing 修饰的变量不是局部变量: 它的生命周期由autoreleasepool负责: +(Reachability*)reachabilityWithHostname:(NSS ...
- AE-----界面介绍
AE-----界面介绍 一.大纲leiji 层级: 比如:高楼一层一层的盖起来的.千层蛋糕(一层一层的).地质(一层一层构造的) 图层的特征:有顺序.上面的一层总会覆盖掉下面的一层. AfterEff ...
- 在centos7上搭建博客之小白教程~
原理 http使用方法一编译安装,php模块打入方式实现. 软件版本 在本次实验中,我们需要用到的软件版本如下: apr-1.6.2 apr-util-1.6.0 httpd-2.4.28 maria ...
- 真机测试出现INSTALL_FAILED_USER_RESTRICTED安装错误
之前用小米测试的时候遇到一个问题,两个一样型号的手机一个能直接用Android Studio安装公司的项目一个却不可以,总是报INSTALL_FAILED_USER_RESTRICTED错误,具体见下 ...
- 详解Tensorflow数据读取有三种方式(next_batch)
转自:https://blog.csdn.net/lujiandong1/article/details/53376802 Tensorflow数据读取有三种方式: Preloaded data: 预 ...
- js 操作字典
有时候我们进行ajax请求的时候,列表并不满足我们需求,有时候需要 key :value形式. 如果还按照python的定义: var data={}; data[tag_id][tag_ch_id] ...
- java学习笔记-基础篇
Java基础篇 1—12 常识 13 this关键字 14参数传递 16 继承 17 访问权限 28—31异常 1—12 常识 1.文件夹以列表展示,显示扩展名,在地址栏显示全路径 2.javac编译 ...
- IE8 下处理select标签高度不居中
IE8: Chrome: 同样的代码可是在IE8下select的文字并不是垂直居中. 处理:给select的样式添加padding上下的内边距. 加line-height一点用都 ...