Unity3D Shader系列之透视效果XRay | 您所在的位置:网站首页 › 透明物体摄影 › Unity3D Shader系列之透视效果XRay |
目录
1 效果展示2 实现原理2.1 渲染队列2.2 深度测试2.3 实现原理
3 源码
1 效果展示
如图。角色被其他物体挡住时,能够看到其基本的轮廓,就像医院的X光一样。所以这种透视效果又被叫做X光。 在说X光的实现原理之前,需要掌握两个储备知识,①是渲染顺序,②是深度测试。 2.1 渲染队列渲染队列用来控制渲染顺序。所谓渲染顺序,即先渲染哪个物体,再渲染哪个物体。 我们可以通过修改Shader中的Queue标签或者材质球中的Render Queue来设置渲染队列,值越小越先渲染。需要注意,如果更改了材质球上的Render Queue值,则该物体的渲染队列以材质球上的Renderer Queue值为准。比如我们这里就将X光物体的材质设置为了Geometry+1。 我们将Robot Kyle使用的材质的Render Queue设置为2001,即Geometry+1,然后点击Frame Debugger的Enable。 Frame Debugger的右侧展示了该相机是如何一步一步渲染的,左侧的数字即为Draw Call数,右侧为渲染某物体时用到的Shader的状态。 ①GPU有一个深度缓冲区(Z Buffer),里面存储的是相机通过该像素点能看到的最近的物体的表面的深度值,所谓的深度值即相机与该物体对应点的距离值,深度值范围一般为0~1,位数为24位。深度值越小,表示距离相机越近。 ②我们可以在Shader中选择是否开启深度测试。 ③如果Shader中开启了深度测试,那么渲染某个物体时,就需要比较深度缓冲区的值与该物体对应的深度值,如果满足某个规则(这个规则也是我们在Shader中指定的,如小于),我们就认为深度测试通过。 ④只有深度测试通过了,才能选择是否进行深度写入(即将该物体的深度值写入到深度缓冲区中)。没有通过深度测试是没有权利进行深度写入的。 举个例子,假设物体A的顶点A1与物体B的顶点B1对应同一个像素,A1的深度值为0.2,B1的深度值为0.5。先渲染你物体A,再渲染物体B。物体A物体B的Shader中都开启了深度测试和深度写入,深度比较规则为小于。那么先渲染A时,由于此时深度缓冲区还没有值,所以A1顶点对应的像素深度测试通过,然后将0.2写入深度缓冲区。物体A渲染完成后再渲染物体B,此时发现B1顶点对应像素的深度缓冲区值为0.2,而0.5>0.2,测试不通过,B1顶点对应的像素颜色将不能输出到屏幕上。 一般使用深度测试的方式来实现。有2个步骤,①先将所有不透明物体全部渲染完成之后再渲染我们的X光物体,②我们的X光物体要渲染两次,第一次渲染先判断物体是否被其他物体挡住,若挡住就渲染物体的边缘,若没挡住则不渲染,而且要关闭深度写入,第二次渲染物体就按正常的渲染。 知道了原理,我们再看看怎么写代码。 步骤①先将所有不透明物体全部渲染完成之后再渲染我们的X光物体: 这个很简单,我们上面已经知道可以使用Unity提供的渲染队列来控制场景中各个物体的渲染顺序。我们这里将X光Shader的渲染队列设置值为2001,即Geometry+1。 步骤②我们的X光物体要渲染两次,第一次渲染先判断物体是否被其他物体挡住,若挡住就渲染物体的轮廓,若没挡住则不渲染,而且要关闭深度写入,第二次渲染物体就按正常的渲染流程,即有物体挡住挡住时则不渲染,没有物体挡住时则渲染 要渲染两次,那就是Shader中有两个Pass(在前向渲染中,一个Pass就代表一次完成的渲染流程,或者说就是经过一次完整的渲染流水线)。第一个Pass要判断是否被其他物体挡住,这简单,在步骤①中我们已经渲染了所有不透明物体,如果深度缓冲区的深度值(存储的是距离相机最近的物体的深度值,深度值越小表示距离相机越近)小于x光物体的深度值,则说明X光物体被其他物体挡住。直接使用ZTest Greater即可实现。 ZTest Greater如果X光物体被其他物体挡住了则要渲染物体的轮廓,这里的轮廓怎么找出来?一般使用边缘光的方式,即当视线方向与模型表面的法线的点积为0时就认为是物体的边缘,是物体的边缘则输出X光颜色。 float3 worldNormal = UnityObjectToWorldNormal(v.normal); fixed3 worldNormalDir = normalize(worldNormal); fixed3 worldViewDir = normalize(WorldSpaceViewDir(v.vertex)); float rim = 1.0 - dot(worldNormalDir, worldViewDir); o.color = _RayColor * pow(rim, _RayPower); 3 源码 Shader "Custom/XRay" { Properties { _MainTex ("Texture", 2D) = "white" {} _MainColor ("Main Color", color) = (1.0, 0, 0, 1.0) _RayColor ("Ray Color", color) = (1.0, 0, 0, 1.0) _RayPower ("Ray Power", Range(0, 3)) = 1.0 } SubShader { LOD 100 CGINCLUDE #include "UnityCG.cginc" struct appdataXRay { float4 vertex : POSITION; float3 normal : NORMAL; }; struct v2fXRay { float4 vertex : SV_POSITION; fixed4 color : TEXCOORD0; }; struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float4 vertex : SV_POSITION; float2 uv : TEXCOORD0; }; sampler2D _MainTex; float4 _MainTex_ST; fixed4 _MainColor; fixed4 _RayColor; float _RayPower; v2fXRay vertXRay (appdataXRay v) { v2fXRay o; o.vertex = UnityObjectToClipPos(v.vertex); float3 worldNormal = UnityObjectToWorldNormal(v.normal); fixed3 worldNormalDir = normalize(worldNormal); fixed3 worldViewDir = normalize(WorldSpaceViewDir(v.vertex)); float rim = 1.0 - dot(worldNormalDir, worldViewDir); o.color = _RayColor * pow(rim, _RayPower); return o; } fixed4 fragXRay (v2fXRay i) : SV_Target { return i.color; } v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); return o; } fixed4 frag (v2f i) : SV_Target { fixed4 col = tex2D(_MainTex, i.uv); return col * _MainColor; } ENDCG // 两个Pass顺序不能交换,一定要先渲染X光再正常渲染物体 Pass { Tags { "RenderType" = "Opaque" "Queue"="Geometry+1" } ZTest Greater ZWrite Off Blend SrcColor OneMinusSrcColor CGPROGRAM #pragma vertex vertXRay #pragma fragment fragXRay ENDCG } Pass { Tags { "RenderType"="Opaque" "Queue"="Geometry"} CGPROGRAM #pragma vertex vert #pragma fragment frag ENDCG } } }项目源码:https://pan.baidu.com/s/1U3T5udbDLYk0maWXu1jW-g 提取码:du3x 博主个人博客本文链接。 |
CopyRight 2018-2019 实验室设备网 版权所有 |