Unity Shader - 对Cubemap进行环境映射(世界空间和切线空间下的对比)_起个名字真的好难啊的博客-程序员宝宝

技术标签: Unity Shader应用  

采样图效果:

 

上面3幅图的效果分别是:原始Cubemap、世界空间下的采样、切线空间下的采样;

由以上对比图可知,在需要使用Cubemap 进行环境映射等情况下,我们就需要在世界空间下对Cubemap进行采样。

切线空间转换到世界空间的方法:

//世界空间下的法线、切线、副法线
float3 worldPos = mul(_Object2World, v.vertex).xyz;  
fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);  
fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);  
fixed3 worldBinormal = cross(worldNormal, worldTangent) * v.tangent.w; 
//构造切线空间到世界空间的旋转矩阵
o.TtoW0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x);  
o.TtoW1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y);  
o.TtoW2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z);  

模型空间转换到切线空间的方法:

//世界空间光源方向(指向光源)
float4 lightDirWorld = float4(normalize(_WorldSpaceLightPos0.xyz), 1);
//从世界空间到观察空间
float4 lightDirObj = mul(lightDirView, UNITY_MATRIX_IT_MV);
float4 lightDirView = mul(UNITY_MATRIX_V, lightDirWorld);
//从观察空间到模型空间
//rotation为模型空间到切线空间的旋转矩阵
TANGENT_SPACE_ROTATION;  
//o.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex));  
o.lightDir = mul(rotation, lightDirObj);
//ObjectSpaceLightDir可以把光线方向转化到模型空间,然后通过rotation再转化到切线空间  
//等同于上面的语句
 //o.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex));  //方式二

完整Shader代码如下:

Shader "Unity Shaders Book/Chapter 15/Water Wave" {
	Properties {
		_MainTex ("Base (RGB)", 2D) = "white" {}
		_WaveMap ("Wave Map", 2D) = "bump" {}
		_Cubemap ("Environment Cubemap", Cube) = "_Skybox" {}
		_WaveXSpeed ("Wave Horizontal Speed", Range(-0.1, 0.1)) = 0.01
		_WaveYSpeed ("Wave Vertical Speed", Range(-0.1, 0.1)) = 0.01
		_Color ("Main Color", Color) = (0, 0.15, 0.115, 1)
	}
	SubShader {
		// We must be transparent, so other objects are drawn before this one.
		Tags { "Queue"="Transparent" "RenderType"="Opaque" }
		
		
		Pass {
			Tags { "LightMode"="ForwardBase" }
			
			CGPROGRAM
			
			#include "UnityCG.cginc"
			#include "Lighting.cginc"
			
			#pragma vertex vert
			#pragma fragment frag
			
			sampler2D _MainTex;
			float4 _MainTex_ST;
			sampler2D _WaveMap;
			float4 _WaveMap_ST;
			samplerCUBE _Cubemap;
			fixed _WaveXSpeed;
			fixed _WaveYSpeed;
			fixed4 _Color;


			struct a2v {
				float4 vertex : POSITION;
				float3 normal : NORMAL;
				float4 tangent : TANGENT; 
				float4 texcoord : TEXCOORD0;
			};
			
			struct v2f {
				float4 pos : SV_POSITION;
				float4 uv : TEXCOORD1;
				float4 TtoW0 : TEXCOORD2;  
				float4 TtoW1 : TEXCOORD3;  
				float4 TtoW2 : TEXCOORD4; 
				float3 lightDir : TEXCOORD5;
			};
			
			v2f vert(a2v v) {
				v2f o;
				o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
				o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
				o.uv.zw = TRANSFORM_TEX(v.texcoord, _WaveMap);

				//世界空间下的法线、切线、副法线
				float3 worldPos = mul(_Object2World, v.vertex).xyz;  
				fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);  
				fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);  
				fixed3 worldBinormal = cross(worldNormal, worldTangent) * v.tangent.w; 
				//构造切线空间到世界空间的旋转矩阵
				o.TtoW0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x);  
				o.TtoW1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y);  
				o.TtoW2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z);  

				//世界空间光源方向(指向光源)
				float4 lightDirWorld = float4(normalize(_WorldSpaceLightPos0.xyz), 1);
				//从世界空间到观察空间
				float4 lightDirView = mul(UNITY_MATRIX_V, lightDirWorld);
				//从观察空间到模型空间
				float4 lightDirObj = mul(lightDirView, UNITY_MATRIX_IT_MV);
				//rotation为模型空间到切线空间的旋转矩阵
				TANGENT_SPACE_ROTATION;  
				o.lightDir = mul(rotation, lightDirObj);  //方式一

                //ObjectSpaceLightDir可以把光线方向转化到模型空间,然后通过rotation再转化到切线空间  
				//等同于上面的语句
                //o.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex));  //方式二
	
				return o;
			}
			
			fixed4 frag(v2f i) : SV_Target {
				float3 worldPos = float3(i.TtoW0.w, i.TtoW1.w, i.TtoW2.w);
				fixed3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));
				
				// Get the normal in tangent space
				fixed3 bump = UnpackNormal(tex2D(_WaveMap, i.uv.zw)).rgb;

				float2 speed = _Time.y * float2(_WaveXSpeed, _WaveYSpeed);
				fixed4 texColor = tex2D(_MainTex, i.uv.xy + speed);
				
				// Convert the normal to world space
				//世界空间下的反射光
				bump = normalize(half3(dot(i.TtoW0.xyz, bump), dot(i.TtoW1.xyz, bump), dot(i.TtoW2.xyz, bump)));
				fixed3 reflDir = reflect(-viewDir, bump);
				fixed3 reflCol = texCUBE(_Cubemap, reflDir).rgb * _Color.rgb;

				//切线空间下的反射光
				//fixed3 reflDir = reflect(-i.lightDir, bump);
				//fixed3 reflCol = texCUBE(_Cubemap, reflDir).rgb * _Color.rgb;

				fixed3 finalColor = reflCol;
				
				return fixed4(finalColor, 1);
			}
			
			ENDCG
		}
	}
	// Do not cast shadow
	FallBack Off
}

世界空间下和切线空间下的效果分别如下面上、下两幅图:

(世界空间)(切线空间)

结论:在需要使用Cubemap 进行环境映射等情况下,我们就需要在世界空间下对Cubemap进行采样。

 

 

UnityShader之空间变换解析

 

我们都知道要渲染出一个物体,需要经历以下空间变换,

模型坐标(通常由美术决定)----->世界坐标----->观察空间(相机位置决定)------------>裁剪空间(相机类型和参数决定)------->投影

unity提供了一系列的矩阵来帮助用户进行坐标空间变换,这里列个表记录下:

 

1:模型空间 2:世界空间 3:观察空间 4:裁剪空间

 

UNITY_MATRIX_MVP:将坐标或者方向从1到4

UNITY_MATRIX_MV:将坐标或者方向从1到3

UNITY_MATRIX_VP:将坐标或者方向从2到4

UNITY_MATRIX_V:将坐标或者方向从2到3

UNITY_MATRIX_P:将坐标或者方向从3到4

 

unity_ObjectToWorld:将顶点或者方向从1到2

unity_WorldToObject:unity_ObjectToWorld的逆矩阵,用于将顶点方向从2到1

重点讲下

UNITY_MATRIX_IT_MV:专门用于将法线从模型空间变换到观察空间,为UNITY_MATRIX_MV的逆转置矩阵,如果UNITY_MATRIX_MV为正交矩阵(意味着只有旋转操作),那么UNITY_MATRIX_MV就等于UNITY_MATRIX_IT_MV,如果有统一缩放操作,UNITY_MATRIX_IT_MV=(1/k)*UNITY_MATRIX_MV ,k为缩放系数((为什么不用UNITY_MATRIX_MV来处理法线,因为如果模型有不统一缩放的时候,用UNITY_MATRIX_MV会造成方向失真),我也可以把这个矩阵转置一下或者UNITY_MATRIX_MV的逆矩阵,就可以把顶点或者方向矢量从观察空间变换到模型空间,例如

float4 modelPos = mul(transpose(UNITY_MATRIX_IT_MV),viewPos)=mul(viewPos,UNITY_MATRIX_IT_MV);

 

 

UNITY_MATRIX_T_MV:UNITY_MATRIX_MV的转置矩阵,因为求一个矩阵的逆矩阵需要巨大的运算量,但是求一个矩阵的转置矩阵就很简单了,所以当一个矩阵为正交矩阵的时候,通过转置矩阵或者逆矩阵是一个很有用的方法,我们在计算一些方向矢量,不考虑平移操作,缩放系数设为1,这样就可以用这个矩阵来计算从观察空间到模型空间的方向矢量变换;

 

 

 

 

 

 

 

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/e295166319/article/details/80840893

智能推荐

三星公司uboot模式下更改分区(EMMC)大小fdisk命令_微尘hjx的博客-程序员宝宝_uboot下给emmc分区

三星公司uboot模式下更改分区(EMMC)大小fdisk命令一、分区三星平台一般把它分为四个区:(1)、fat分区,作为sd卡用;(2)、系统分区,相当为电脑c 盘,用来安装android系统;(3)、userdata分区;(4)、cache分区。二、分区更改操作过程1,  更改uboot中代码/common/cmd_m

vim使用技巧_aezympxj84131的博客-程序员宝宝

vim使用技巧 #------------------------------------------------/joe/e ;: 设置光标到匹配"joe"的末尾 /joe/e+1 ;: 设置光标到匹配"joe"的末尾再后移一位 /joe/s-2 ;: 设置光...

前端学习笔记一一HTML表单标签(form)_专注Python二十年的博客-程序员宝宝

form表单细节一、表单1.表单 标签用于为用户输入创建 HTML 表单2.表单能够包含 input 元素,比如文本字段、复选框、单选框、提交按钮等等。3.表单还可以包含 menus、textarea、fieldset、legend 和 label 元素。4.表单用于向服务器传输数据。二、表单form 的属性(一)action属性action 属性规定当提交表单时,向何处发送表单数据属性值:url 链接的地址(二)name属性na

org.apache.commons.logging.Log_普通网友的博客-程序员宝宝_org.apache.commons.logging.log

将JCL日志整合到log4j2统一输出,需要引入log4j2提供的依赖包: <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-jcl</artifactId> <version>2.6.2</version> </dependency&g

获取url地址栏信息,含中文_de_shy的博客-程序员宝宝

function GetQueryString(name) { var reg = new RegExp("(^|&)"+ name +"=([^&]*)(&|$)"); var r = window.location.search.substr(1).match(reg); if(r!=null)return decodeURI(r[2]); return null; }

【Git】撤销已经git add的文件_寒泉Hq的博客-程序员宝宝

参考文章:git commit和git add的撤销如果不想提交某个文件,比如DBConfig.py,但是不小心git add了这个文件,想要撤销add的话:不要用git rm --cached DBConfig.py,应该用git reset HEAD DBConfig.pygit add的撤销git add 操作时,会将工作目录中已修改的文件放到暂存区。这时如果你误添加一些不想提...

随便推点

python中使用opencv无法打开usb摄像头的问题_以自由之名的博客-程序员宝宝_python opencv打开usb摄像头

今天终于解决了自己在python中使用opencv无法打开usb摄像头的问题,太坑了,感觉自己好蠢,哈哈哈,特此记录。1.首先是打开在python中使用opencv打开电脑摄像头的代码如下:#1.引入cv2import cv2#2.主函数if __name__ == '__main__':#3.打开笔记本摄像头cap = cv2.VideoCapture(0)assert cap.isOpened(), 'Cannot capture source' #摄像头没有正常打开则报错#4.

android刷礼物动画demo,GiftSurfaceView 直播间送礼物动画_weixin_39611070的博客-程序员宝宝

GiftSurfaceViewGiftSurfaceView 最初出自于2014年开发HalloStar项目时所写,主要用于HalloStar项目直播间的送礼物动画。现在想来,那夕阳下的奔跑,是我逝去的青春。因前几天,刚高仿全民TV,所以想起,稍加改善,以此记录。Gif展示引入Maven:com.king.viewgiftsurfaceview1.1.0pomGradle:compile 'com...

特征提取方法_zhao_crystal的博客-程序员宝宝_特征提取

在图像识别方向,可通过sift,surf,orb等算法提取特征,然后再喂给一个中等粒度的vector2算法,最后再去做分类。1. sift1.1 sift特征简介SIFT(Scale-Invariant Feature Transform)特征,即尺度不变特征变换,是一种计算机视觉的特征提取算法,用来侦测与描述图像中的局部性特征。实质上,它是在不同的尺度空间上查找关键点(特征点),并计算出关键点的方向。SIFT所查找到的关键点是一些十分突出、不会因光照、仿射变换和噪音等因素而变化的点,如角点

Protocol Buffer Basics: C++中文翻译(Google Protocol Buffers中文教程)_codelast.com的博客-程序员宝宝

注:这是本人的翻译,可能不准确,可能有错误,但是基本上可以理解,希望能对大家有所帮助!(转载请注明出处:本文来自learnhard的程序员宝宝:http://blog.csdn.net/learnhard/)Protocol Buffer Basics: C++(Protocol Buffer基础:C++篇)This tutorial provides a basic C++ programmer's introduction to working with protocol buffers. By wa

洛谷 [P2886] 牛继电器Cow Relays_aiwa6731的博客-程序员宝宝

最短路 + 矩阵快速幂我们可以改进矩阵快速幂,使得它适合本题用图的邻接矩阵和快速幂实现注意 dis[i][i] 不能置为 0#include <iostream>#include <cstdio>#include <algorithm>#include <cmath>#include <cstring>#inc...

Silverlight输入数据验证系列二_loveheye的博客-程序员宝宝

Silverlight实例教程 - Validation数据验证DataAnnotation机制和调试技巧时间:2010-09-09 21:18来源:SilverlightChina.Net 作者:Jv9 点击: 390次在学习了Silverlight Validation数据验证基础属性和事件后,大家对Silverlight数据验证应该有了一个简单明了的认识。今天,我将继续介绍另外一种Silverlight的Validation验证机制,DataAnnotation。<br /> <br /> <br /

推荐文章

热门文章

相关标签