开启辅助访问
 找回密码
 注册帐号

扫一扫,访问微社区

开发者专栏

关注:1668

当前位置:游戏蛮牛 技术专区 开发者专栏

__________________________________________________________________________________
开发者干货区版块规则:

  1、文章必须是图文形式。(至少2幅图)
      2、文章字数必须保持在1500字节以上。(编辑器右下角有字数检查)
      3、本版块只支持在游戏蛮牛原创首发,不支持转载。
      4、本版块回复不得无意义,如:顶、呵呵、不错......【真的会扣分的哦】
      5、......
__________________________________________________________________________________
查看: 3469|回复: 22

[蛮牛干货] 【UnityShader从零开始】漫反射效果

[复制链接]  [移动端链接]
抢楼 抢楼 本帖为抢楼帖,欢迎抢楼! 
排名
6414
昨日变化
1

9

主题

86

帖子

637

积分

Rank: 9Rank: 9Rank: 9

UID
15359
好友
9
蛮牛币
940
威望
0
注册时间
2014-2-23
在线时间
171 小时
最后登录
2017-3-13

蛮牛译员游戏蛮牛QQ群会员VIP活力之星

发表于 2015-4-6 13:34:08 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?注册帐号

x
本帖最后由 esfog 于 2015-4-8 13:00 编辑

  原文地址http://www.cnblogs.com/Esfog/p/3577412.html.有任何问题和意见都可以在帖子下方留言,也欢迎光临我的Blog与我讨论.

      经过上两篇的介绍,相信大家已经对UnityShader有一些了解了,我们从这篇开始就不会再专门纠缠语法了,一般都会在用到的时候特殊说明一下.如果你还对UnityShader的基础语法比较陌生,那么推荐看一下本系列的前两篇文章.

漫反射DiffuseReflection


   光照是图形渲染里的一个重要课题,处理的好坏直接影响了游戏展示给玩家的场景效果,做的越好越真实,给玩家代入感也就越强烈,一般的光照模型中包括4种:环境光,自发光,漫反射,高光。而光照处理里面两个最常见的课题也就是漫反射和镜面反射(高光),这一篇我们讨论漫反射,关于镜面反射的相关内容将在下一篇中讲解.提到漫反射大家一定不会陌生,因为我们在小学或是初中就一定知道了什么是漫反射和镜面反射了.不过为了下面讲解的让大家更容易理解,我还是简单的用自己的语言描述一下漫反射的概念.
    001.jpg
  如上图(图片来自网络),当光照射到物体表面时,由于物体表面的凹凸不平,导致光向各个方向反射出去,在理想状态下,我们认为光向各个方向反射的量是相同的,不难理解,这样就会无论我们从哪个角度观察物体,物体上的某一点看上去都是一样亮的.也就是说我们的眼睛从各个角度接收到来自这个点的光线量都是相同.在游戏里,摄像机也就相当于我们的眼睛,那我们要做的也就是计算出光照在物体表面后,在每个像素上的反射强度,无论我们从什么角度看,都让它始终保持这个值.原理就说到这里,如果你对我说的不理解,那也无妨,总之你知道漫反射就是无论你从哪个角度看,看到某个点的亮度都应是一样的就可以了.下面来我们来看具体的计算原理.
   QQ截图20150406231612.png
  如上图(图片取自《Cg Programming in Unity》),要计算漫反射,我们需要知道两个量一个是物体表面的法线N,另一个是光的入射方向L(注意,我们这里考虑的只有平行光DirectionalLight,对于点光源等其他光源的计算方式略有不同,读者可自行搜索了解),
  在写具体代码之前,我们先说明一下计算的原理,原理就是我们通过计算光的入射方向(这里所谓的入射其实是入射方向的反方向,之所以这样是为了方便计算)和物体表面向量的夹角来决定这点的光照强度,两个向量的夹角越小就说明越来越接近于关照直射,这时候当然反射的光也就越强,如果夹角大于等于90度那么反射的光强度越来越弱,超过90度就完全看不到了.额外说一点:也许你会纠结,前面明明说我们假设光在任何方向上的反射都是同量的,而且物体表面的凹凸不平也应该是平均的,那么为什么直射的地方就一定比其它地方看上去量的,说实话这个问题一开始纠结了我很久,它超出了我对Shader的理解范围,偏大到物理方面了,后来我个人认为可能是由于夹角越大的时候光在向各个方向反射的时候表面内部光来回传递所消耗的能量就越多,最后反射出去的也就越少了.这只是我的个人理解,如果哪位有更权威的解释,请评论告诉我,如果你压根没有把这当成一个我问题就不要去思考他了,我这个人比较爱钻牛角尖,什么都爱刨根问底,也不知道是好是坏.
  那么在通过夹角来计算光的亮度之前,我们需要对N和L两个向量进行归一化处理,如果大家学过线性代数或者高中数学没忘干净的话,那么一定对它不陌生,如果你实在记不起来就去上网搜搜吧.在CG里对向量进行归一化的操作我们使用normalize函数,这是CG数学库提供给我们的.然后我们利用向量的点积公式N·L = |N|*|L|*cosθ(如果你对点积也不了解,我就不过分解释了,上网搜一下).由于我们刚刚对N和L进行了归一化,他们的模就是1了,那么N·L = cosθ了.也就是说我们直接对N和L进行点积运算会得到两个向量的夹角余弦值,我们知道如果加角θ越接近月0°那么N·L也就越接近于1,越接近月90°,N·L就越接近于0.所以我们只要把光的颜色乘以这个点积结果就会达到我们想要的角度越小光越强,角度越大光越弱的效果了.下面给出《The Cg Tutorial》中给出的漫反射计算公式:
  diffuse = Kd * lightColor * max(N·L,0)
  下面我们就来通过实际代码来讲述一下具体的计算过程.

[C#] 纯文本查看 复制代码
Shader "Esfog/Diffuse" 
{
    Properties 
     {
                 _MainTex ("Base (RGB)", 2D) = "white" {}
             }
     SubShader 
     {
         Pass
         {
             Tags { "RenderType"="Opaque" "LightMode"="ForwardBase"}
             CGPROGRAM
             #pragma vertex vert
             #pragma fragment frag
             #include "UnityCG.cginc"
                
             uniform sampler2D _MainTex;
             uniform float4    _LightColor0;
             struct VertexOutput 
             {
                 float4 pos:SV_POSITION;
                 float2 uv_MainTex:TEXCOORD0;
                 float3 normal:TEXCOORD1;
                  };
 
             VertexOutput vert(appdata_base input)
             {
                 VertexOutput o;
                 o.pos = mul(UNITY_MATRIX_MVP,input.vertex);
                 o.uv_MainTex = input.texcoord.xy;
                 o.normal = normalize(mul(float4(input.normal,0),_World2Object));
                 return o;
              }
 
             float4 frag(VertexOutput input):COLOR
             {
                 float3 normalDir = normalize(input.normal);
                 float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
                 float3 Kd = tex2D(_MainTex,input.uv_MainTex).xyz;
                 float3 diffuseReflection = Kd * _LightColor0.rgb * max(0,dot(normalDir,lightDir));
                 return float4(diffuseReflection,1);
             }
             ENDCG
         }
     } 
     FallBack "Diffuse"
}

  这里我假定大家已经看过我的上一篇教程或者有一定的Shader语法基础,所以不会像上一篇一样一行行解释,只选择与本篇相关的或者新出现的内容进行说明.

  第11行 大家会发现我们的Tags里面比上一次多了一个"LightMode"="ForwardBase",这句话的作用其实是和Unity处理场景中的所有光源的方案有关系的,一般默认使用的处理方式为Forward(具体细节请参考官方文档Unity's Rendering behind the scenes一节的内容).如果处理方式为Forward那么当我们在Tags里写上这一句的时候,简单的理解为,当渲染这个物体的时候场景中的第一个平行光源的一些参数我们可以直接在这个Pass中使用.包括光的颜色_LightColor0,光的在世界空间的位置_WorldSpaceLightPos0等.如果你不适用这种方法的话你也可以通过在外部写一个脚本将光的一些参数和任何你想传送的变量传送到Shader中,具体方式我们在日后遇到相关问题的时候具体说.

  第18行 float4 _LightColor0;我们定义了一个新的_LightColor0来接收存储光照的颜色,我们并没有在Properties中定义也没有通过外部脚本来传值,那它是做什么用的呢?其实由于我们上边在Tags中添加的新标签,所以_LightColor0会被Unity自动赋值为场景中第一个平行光的颜色(这也不一定,其实和光源的RenderMode属性有关,但如果你不做修改的话,那就是用第一个光源).

  第23行 float3 normal:TEXCOORD1;我们在顶点着色器的返回结构中多添加了一个变量,看名知意我们要把顶点的发线经过插值后传到片段着色器中,我们之前说过TEXCOORD0~TEXCOORDX(具体视显卡能力而定),可以用来保存任何我们需要插值的内容,由于我们想在片段着色器中来计算物体表面的漫反射所以就需要将法线传过去.
  那么有一个很值得思考的问题:"为什么要在片段着色器中计算漫反射呢?",这是个很好的问题,在到底在顶点着色器还是片段着色器中来进行光照的处理并无一个明确的规定,这是一个对于性能和效果的权衡,如果在顶点着色器中计算的话很显然我们的计算次数和顶点的数量一致,也就是很少,但是效果就不好,一般来说一个像素所在的三角片上的三个顶点对光源的捕捉有可能不足,就会导致最后用三个顶点计算出来的漫反射颜色来插值出的面片颜色就会不准确,也就会出现本该很亮的地方却不亮.所以说在顶点着色器中进行光照计算的性能会很高(因为计算次数远远小于片段着色器),但是效果差,而在片段着色器中计算则正相反,性能会很低,但是效果会很好(因为每个像素都是单独通过法线来计算的,而不是直接用顶点插值出来的).所以具体情况具体分析.

  第31行o.normal = normalize(mul(float4(input.normal,0),_World2Object));这一句中我们把模型本身自带的顶点法线属性,先进行空间变换将其变换到世界空间中去,为什么要换到世界空间中呢,其实只要保证要进行操作的两个向量在同一个空间,那么具体是哪个空间并不重要,不过由于Unity为我们提供的大量参数都是在世界空间中的,所以我们就变换到世界空间中去吧,这里还有一点很重要,向量的空间变换与点不同,它要右乘上目标变换矩阵的逆的转置,具体的数学原因有些复杂,大家可以自己查一下,那么原来的目标矩阵式模型空间到世界空间unity中为我们提供了这个矩阵_Object2World,不过我们要的不是它,我们先来求他的逆,unity也为我们提供了_World2Object,其实严格上来讲这并不是_Object2World的逆,我们呢要将这个矩阵*unity_Scale.w之后再将矩阵的最右下角的数置为1.这其中的问题我只是在国外的论坛上看到过一些解释,我也无法很准确的表达出来,等以后我弄明白了再告诉大家,如果有人知道也请您一定告诉我,不过为什么这里我们没有进行刚才的几步操作呢,因为这两步操作都是针对缩放的,由于我们马上要对他进行归一化所以对我们只要他的方向正确就好,大小无所谓。这样我们得到了它的逆,我们要继续求它的转置,不过这个就不需要了,之前我们都是将矩阵放在mul函数的左边,而向量或点放在右侧,我们调换一下他们的位置就相当于乘上了转置(不明白就看看线性代数吧),由于mul要求点或者向量必须表示成4维的,所以我们将他转换成float4并将最后一位填0(原因后面有解释).上述结束以后我们进行了归一化,传给了顶点输出结构中相应变量,这样做只为了保证在插值的时候不同顶点之间的法线是在同一个标准下进行的.因为法线只起到方向的作用,如果有的顶点的法线长度太长,而有的很短,那么各个分量在进行差值的时候就会出现错误的结果,当然一般美术同学做出来的模型自带的法线属性都是单位向量,所以这里只是为了以防万一.

  第37行 float3 normalDir = normalize(input.normal); 我们定义个了一个float3变量来保存插值后在当前片段上的法线,由于经过插值会使我们原本的单位向量不再是单位向量(如果你想知道具体原因,就想一下插值过程知识满足了各个分量的插值结果,但并不能保证整体上还是一个单位向量),所以我们需要再次进行归一化.

  第38行float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);我们又定义了一个float3变量来保存入射光的方向(反方向),这里有一些需要说明的,Unity中的平行光源的位置是没有意义的,只有它的方向代表了光线的方向.而在3D数学中,点和向量的概念有时候是很模糊的.为了区分它们,在齐次空间中我们用一个4维的行(列)向量来表示它们,xyz分量都相同,只有w分量不同,点的w分量为1,向量的w分量为0,如果想知道具体原因的话,就去看看线性代数吧,这个不太好解释.而我们的平行光源其实他可以看做一个向量,他具体在哪没有意义,我们要的只是一个方向,_WorldSpaceLightPos0是Unity为我们赋值的一个表示场景中第一个平行光的位置(这里不一定是第一个, 解释通上面的光照颜色).由于位置无关,那么我们就直接取出他的前三个分量,把它看做一个由世界空间原点到光源位置的一个向量,然后我们再对他进行归一化最终就得到我们想要的结果了,也许这里我也解释的不是很清楚,我自己理解这里的时候也不是一下子就明白的.

  第39行float3 Kd = tex2D(_MainTex,input.uv_MainTex).xyz;这个Kd就是我们在上面一开始提到的那个书中给出的计算漫反射的公式,其中Kd表示材质的漫反射颜色,这个说法很有迷惑感,我个人是这样理解的,根据物理上的说法,物体本身如果没有光照射,它是呈现不出任何的颜色的,而最终表现给眼睛的颜色实际上是光源找到物体表面没有被物体表面吸收而反射回来的颜色,那么我们这个Kd不如理解成将光吸收后反射出去的颜色成分(其中3个分量分别对应对RGB三种颜色值的反射量).如果你不纠结于此就当我没说.总之我们这里讲Kd直接赋成我们的纹理颜色就可以了,你就理解成物体本身就这个颜色也成.

  第40行就是利用了我们一开始的公式将光照颜色计算出来,有三个地方需要说明,其中_LightColor是光源的颜色,这个在上面解释过了,至于为什么要和Kd相乘,我个人觉得这是和色彩处理相关的,因为相乘的情况下颜色的结合看起来是最自然的和真实世界中最相近.第二个地方就是dot(normalDir,lightDir),这个dot是Cg提供给我们来计算两个光源的点积的,为什么这么做一开始我们说过了,最后一点就是max函数,之所以将点积计算出来之后还要和0去取一个最大值,主要是为了避免当normal和lightdir的夹角大于90度的时候点积计算出现负值,会导致整个漫反射颜色计算出来是个负值进而导致整体的光照计算出现错误,当大于90度的时候漫反射颜色已经失去意义,所以需要用max函数保证不会出现负值.

  (~ o ~)~好了系列教程的第三篇到此结束了,和前两篇基础的不同,这一次可能涉及到一些数学概念和Unity的东西所以我描述起来和大家理解起来都比较费劲,不过没什么东西是一蹴而就的,我给大家写一篇文章的背后,我自己都不知花了多久去弄清楚一个概念,去理解一个公式.总之希望大家不要因为一时的不理解而放弃学习和探索。你选择学习Shader就说明你一定是对游戏开发又更高追求的人,成功从来都是不易的,那些唾手可得的东西并没有什么值得让人骄傲和羡慕的.希望大家和我一道继续向前向前!
  下面是两幅图来展示一下效果:
003.png
上面是使用的我们上篇教程所讲述的直接使用贴图颜色的效果
004.png

上面这张是使用了我们上面写的Shader.有了明暗效果是不是看上去更真实了一些了,当然你可能觉得效果并没有什么特别好,因为漫反射要和其他光照处理一起使用才更加完美.我们这里只用了漫反射,连环境光都没有使用,所以并不是特别理想.

Add:2015/4/8添加一张为什么法线变换需要乘以矩阵逆的转置的简单推导,如果想了解可以看我在下面回复问题中的解释


unity 5.4 rendermode;unity 两个向量求夹角;unity 向量的模;unity rendermode;unity 求两向量的夹角;unity 向量模
IMG_1226.JPG

评分

参与人数 8蛮牛币 +30 鲜花 +23 收起 理由
qaz6889845 + 1 很给力!
940718633 + 1 很给力!
asai8diao2410 + 5 赞一个!
ingingpk + 4 赞一个!
杰之行008 + 2 赞一个!
EveryTime + 5 赞一个!
a99677137 + 5 很给力!
优弧 + 30 非常给力。感谢。

查看全部评分

本帖被以下淘专辑推荐:


回复

使用道具 举报

排名
600
昨日变化
1

0

主题

78

帖子

1461

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
2321
好友
0
蛮牛币
2708
威望
0
注册时间
2013-8-20
在线时间
345 小时
最后登录
2017-4-20
发表于 2015-4-6 16:34:20 | 显示全部楼层
写的不错~加油,以前学习的时候也想写来着,就是实在没有时间来码字啦

回复

使用道具 举报

2初来乍到
146/150

0

主题

27

帖子

146

积分

Rank: 2Rank: 2

UID
16778
好友
0
蛮牛币
217
威望
0
注册时间
2014-3-7
在线时间
119 小时
最后登录
2016-11-15
发表于 2015-4-7 14:59:19 | 显示全部楼层
问题很多,我一个一个问
一个向量如果能左乘一个矩阵,那这个向量必须是列向量,
一个向量如果能右乘一个矩阵,那这个向量必须是行向量,
那么29行和31行,为什么顶点是列向量,而法线是行向量?

回复

使用道具 举报

2初来乍到
146/150

0

主题

27

帖子

146

积分

Rank: 2Rank: 2

UID
16778
好友
0
蛮牛币
217
威望
0
注册时间
2014-3-7
在线时间
119 小时
最后登录
2016-11-15
发表于 2015-4-7 17:09:45 | 显示全部楼层
本帖最后由 nightmareroy 于 2015-4-7 17:37 编辑

还有,第31行完全不能理解,我觉得向量和点的空间变换是一样的。
改成如下,效果是一样的:
o.normal=mul(_Object2World,float4(input.normal,0)).xyz;
这个就比较好理解,就是把法向量左乘MVP中的M矩阵,变到世界坐标系下。
也不需要单位化,因为你在frag里面有单位化。





回复

使用道具 举报

排名
6414
昨日变化
1

9

主题

86

帖子

637

积分

Rank: 9Rank: 9Rank: 9

UID
15359
好友
9
蛮牛币
940
威望
0
注册时间
2014-2-23
在线时间
171 小时
最后登录
2017-3-13

蛮牛译员游戏蛮牛QQ群会员VIP活力之星

 楼主| 发表于 2015-4-7 20:49:55 | 显示全部楼层
nightmareroy 发表于 2015-4-7 14:59
问题很多,我一个一个问
一个向量如果能左乘一个矩阵,那这个向量必须是列向量,
一个向量如果能右乘一个矩 ...

先来回答这个问题:
你说的没错,Nvidia的CG官网文档(http://http.developer.nvidia.com/Cg/mul.html)上对mul有一段解释如下:
Returns the vector result of multiplying a matrix M by a column vector v; a row vector v by a matrix M; or a matrix A by a second matrix B.
也就是说对于一个向量你把它当成行向量还是列向量都是无所谓的,因为在内存中存储的时候都是线性存储。在三维空间中就是个向量,至于把它作为行向量还是列向量完全是看你自己的需求。

对于Unity来说由于它是左手坐标系的,所以默认它在进行矩阵变换的时候一律都是进行左乘,也就是mul(MVP,vertex).
点和向量在变换的时候其实是一致的都是进行左乘,为什么29行和31行会出现不同的,那是因为31行的向量是法向量,它很特殊,必须进行这样的特殊处理,具体我在回答下一个问题的时候会说明。

回复

使用道具 举报

排名
6414
昨日变化
1

9

主题

86

帖子

637

积分

Rank: 9Rank: 9Rank: 9

UID
15359
好友
9
蛮牛币
940
威望
0
注册时间
2014-2-23
在线时间
171 小时
最后登录
2017-3-13

蛮牛译员游戏蛮牛QQ群会员VIP活力之星

 楼主| 发表于 2015-4-7 21:22:48 | 显示全部楼层
nightmareroy 发表于 2015-4-7 17:09
还有,第31行完全不能理解,我觉得向量和点的空间变换是一样的。
改成如下,效果是一样的:
o.normal=mul(_ ...

接着上面的问题一起解释,其实这两个问题的的迷惑点是一致的,在于法向量在进行矩阵变换的特殊性.
先说明一下,其实你上面写的式子也是对的,为什么呢?因为Unity帮我们解决了这个令人迷惑的地方。如果Unity没有帮我们解决的话,你的这种写法就会有问题。
问题解释起来比较复杂,都是数学上的问题。
推荐你看下面几篇文章
http://www.cnblogs.com/mengdd/archive/2011/08/30/2598025.html
http://blog.csdn.net/rabbit729/article/details/6040630
大概的意思就是说,如果是对模型在各个轴向上进行非等比缩放的话,虽然顶点的变换不会有问题,但是对于法线就会导致不在垂直于原表面,这样子肯定就错了,因为之所以叫法线就是因为它是垂直于表面的。所以在对法线进行本地到世界的变换需要把normal左乘上_Object2World的逆矩阵的转置矩阵,而_Object2World的逆矩阵就是_World2Object,但是转置矩阵没法直接得到,不过我们可以通过调换向量和矩阵的相乘顺序而到达同样的目的,所以你就看到了我写的那个mul(float4(input.normal,0),_World2Object)。

那为什么我说你写的也对呢,你可以参考一下下面的这篇帖子
http://stackoverflow.com/questions/25609762/arent-normals-read-from-normal-semantic-in-object-space
发现没有Unity在进行缩放的时候不会进行非等比缩放,即使你在编辑器里面调成非等比的它也会通过相应算法变换让他看起来还是等比缩放。这样子也就不存在上面我说的那个问题了,所以你说的,和我说的都是可以的。

至于你说的不需要单位化,我是这样理解的,对于法线,我们需要的只是方向,如果说在顶点着色器中进行了空间变换的法线没有进行归一化,那么可能就会导致每个顶点所计算出来的法线向量并不是一致长度的.(当然了,我上面也说了,Unity里面都是等比缩放,所以大家得到的长度都应该是一致的)。如果各个顶点法线向量长度不一致,那么在进行下一步的光栅化中对法线进行插值的时候就会由于参与插值的各个顶点的法线量长度不一致(也就是各个顶点不在一个基准上),就会导致最终插值出来的结果不仅受到了方向的影响,还受到了长度的影响。这样就会导致最终进入片元着色器的法向量是错误的。
    所以如果在顶点着色器里面进行归一化以后,那么插值的时候大家的长度都是1,只是方向不同,也就不会有问题了。不过还是由于Unity会优化非等比缩放的问题,所以这个问题也就不是问题了。

啰嗦了一堆,不知道有没有解答你的疑惑。
你看的很用用心啊,这两天帖子你都可以总结出问题,而且都是很有针对性的问题,很不错,谢谢你的关注。
如果有不明白的可以继续讨论,也可以去我的Blog留言。
我也收拾收拾准备回家了

回复

使用道具 举报

5熟悉之中
800/1000
排名
2094
昨日变化
1

0

主题

100

帖子

800

积分

Rank: 5Rank: 5

UID
27324
好友
2
蛮牛币
3313
威望
0
注册时间
2014-5-29
在线时间
228 小时
最后登录
2017-4-18
发表于 2015-4-8 10:30:09 | 显示全部楼层
esfog,贴吧的大牛啊,在这也能见到你!

回复

使用道具 举报

2初来乍到
146/150

0

主题

27

帖子

146

积分

Rank: 2Rank: 2

UID
16778
好友
0
蛮牛币
217
威望
0
注册时间
2014-3-7
在线时间
119 小时
最后登录
2016-11-15
发表于 2015-4-8 11:13:39 | 显示全部楼层
本帖最后由 nightmareroy 于 2015-4-8 11:31 编辑

哦,看了你给的第一篇文章,明白了这里居然有这么多玄机,法向量的处理每一步都是大有文章,好贴要顶。
不过整理一下思路,发现仍然是有问题的,就是这种求逆求转置的处理,需要右乘的矩阵,应该是对scale做非等比例变换的矩阵的逆,而不应该是M矩阵的逆,因为法线在做了非等比例变换之后,在局部坐标系下就已经出现错误了,其实是跟MVP变换无关的,你觉得呢?
还是难道说,这个M矩阵,就已经包含了非等比例缩放变换?我觉得这个可能性不大吧。。
求解求解。。

哦我的错,对scale做非等比例变换的矩阵就是M矩阵。。

回复

使用道具 举报

2初来乍到
146/150

0

主题

27

帖子

146

积分

Rank: 2Rank: 2

UID
16778
好友
0
蛮牛币
217
威望
0
注册时间
2014-3-7
在线时间
119 小时
最后登录
2016-11-15
发表于 2015-4-8 11:27:18 | 显示全部楼层
本帖最后由 nightmareroy 于 2015-4-8 11:52 编辑

那么其实MVP这三个矩阵,是不是只有M矩阵才对scale有非等比例拉伸?我记得P矩阵好像也有非等比例拉伸的吧?P矩阵要处理吗?

回复

使用道具 举报

排名
6414
昨日变化
1

9

主题

86

帖子

637

积分

Rank: 9Rank: 9Rank: 9

UID
15359
好友
9
蛮牛币
940
威望
0
注册时间
2014-2-23
在线时间
171 小时
最后登录
2017-3-13

蛮牛译员游戏蛮牛QQ群会员VIP活力之星

 楼主| 发表于 2015-4-8 12:00:18 | 显示全部楼层
nightmareroy 发表于 2015-4-8 11:27
那么其实MVP这三个矩阵,是不是只有M矩阵才对scale有非等比例拉伸?我记得P矩阵好像也有非等比例拉伸的吧? ...

P是投影变换,它非常特殊,而且在构造上很有技巧性。和M和V不是一个理解程度上的。
另外对于你上面的问题,我给你的文章链接可能只是用语言描述,没有公式推导理解起来不直观。
我刚才在纸上写了一下,不出意外应该是对的。
我先去吃饭,一会回来给你传一下图,再回复你!

回复

使用道具 举报

3偶尔光临
271/300
排名
6414
昨日变化
1

0

主题

38

帖子

271

积分

Rank: 3Rank: 3Rank: 3

UID
39085
好友
0
蛮牛币
437
威望
0
注册时间
2014-8-10
在线时间
99 小时
最后登录
2017-4-20
发表于 2015-4-8 12:40:03 | 显示全部楼层
学习了!最近想学这方面的东西

回复

使用道具 举报

排名
6414
昨日变化
1

9

主题

86

帖子

637

积分

Rank: 9Rank: 9Rank: 9

UID
15359
好友
9
蛮牛币
940
威望
0
注册时间
2014-2-23
在线时间
171 小时
最后登录
2017-3-13

蛮牛译员游戏蛮牛QQ群会员VIP活力之星

 楼主| 发表于 2015-4-8 13:05:34 | 显示全部楼层
nightmareroy 发表于 2015-4-8 11:13
哦,看了你给的第一篇文章,明白了这里居然有这么多玄机,法向量的处理每一步都是大有文章,好贴要顶。
不 ...

我在文章最后补上了一张补充说明的推导图片,最后可以看出,我们本来相对normal进行的变换矩阵就是旋转不变,而缩放取逆。
我图片上的证明过程可以看出对原来的变换矩阵整体求逆的转置实际上就是只让缩放取了逆而旋转不变。
希望对你有所帮助,如果我有说的不对的地方也希望指出。
感谢你一直以来的提问,让我在回答的时候也能有所收获。
希望能持续关注。

回复

使用道具 举报

2初来乍到
146/150

0

主题

27

帖子

146

积分

Rank: 2Rank: 2

UID
16778
好友
0
蛮牛币
217
威望
0
注册时间
2014-3-7
在线时间
119 小时
最后登录
2016-11-15
发表于 2015-4-8 15:09:57 | 显示全部楼层
esfog 发表于 2015-4-8 13:05
我在文章最后补上了一张补充说明的推导图片,最后可以看出,我们本来相对normal进行的变换矩阵就是旋转不 ...

哦,看了看了,是正确的,太棒了,赞一个!

回复

使用道具 举报

5熟悉之中
925/1000
排名
1721
昨日变化
1

0

主题

118

帖子

925

积分

Rank: 5Rank: 5

UID
2012
好友
2
蛮牛币
1927
威望
0
注册时间
2013-8-14
在线时间
255 小时
最后登录
2017-3-29

社区QQ达人游戏蛮牛QQ群会员

发表于 2015-4-8 17:34:02 | 显示全部楼层
赞楼主。。。。。。顶一个,写的很棒

回复

使用道具 举报

6蛮牛粉丝
1055/1500
排名
1393
昨日变化
1

0

主题

107

帖子

1055

积分

Rank: 6Rank: 6Rank: 6

UID
28972
好友
2
蛮牛币
3107
威望
0
注册时间
2014-6-10
在线时间
302 小时
最后登录
2017-3-15
QQ
发表于 2015-4-8 18:32:07 | 显示全部楼层
本帖最后由 Guitars 于 2015-4-8 18:33 编辑

[C#] 纯文本查看 复制代码
[mw_shl_code=csharp,true]using UnityEngine;
using System.Collections;


public class ChocolateBoiler
{
    private bool empty = false;
    private bool boiled = false;

    private volatile static ChocolateBoiler instance;
    public static ChocolateBoiler instances()
    {
        if (instance == null)
        {
            lock (instance)
            {
                if (instance == null)
                {
                    instance = new ChocolateBoiler();
                }
            }
        }
        return instance;
        
    }
    private ChocolateBoiler()
    {
        empty = true;
        boiled = false;
    }

    public void fill()
    {
        if (isEmpty())
        {
            empty = false;
            boiled = false;
        }
    }

    public void drain()
    {
        if (!isEmpty() && isBoiled())
        {
            empty = true;
        }
    }

    public void boil()
    {
        if (!isEmpty() && isBoiled())
        {
            boiled = true;
        }
    }
    public bool isEmpty()
    {
        return empty;
    }

    public bool isBoiled()
    {
        return boiled;
    }
}
[/mw_shl_code]

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册帐号

本版积分规则

快速回复 返回顶部 返回列表