这个月进入了找不倒工作的焦虑状态,花了两周时间去学OpenGL,发现以前课上的讲的内容过于浅显,也加深了对渲染管线的了解也算是相当不错的吧。
话不多说,先上最初的效果图吧。
本效果考了该篇博客Unity Shader-Decal贴花
大佬的一篇博客的n个效果都够我研究n回写n篇博客了,请收下我的膝盖。

貌似跟上次MeshDecal的效果差不多(祖传贴图拉长还在),但是这次只用了一个Shader就实现了,真是tql,“还要啥自行车!”。
我先来讲讲思路吧,原文章就写了个三角形相似,我想了许久才知道什么原理,我个人感觉还是比较绕而且不太好理解。

我们就是要通过B点求D点,C点(该死的试用水印把C点挡住了,见谅)求E点,图画得不太好也请见谅。明显可以看到我们可以构建等比三角形OCa和OEb,其实我个人认为也可以把OC与OE理解为两组向量,我们可以通过比例用OC求出OE。
那怎么求呢?说了是用比例,那么用的是哪个比例?我们能用的大概也就是Z坐标了,我们获取深度缓冲里的深度值,并将它还原到视角空间下的Z值(即上图D或E的值),然后将B或C转换到视角空间,这样通过他们z值的比例我们就可以通过B,C求出D,E。
上图出现了三种情况,一是A点对应的点不在圆柱上,二是D在圆柱上且在正方形内,三是E点在圆柱上但是不在正方形上,我们把一三情况的片元剔除掉,只保留第二种情况,就造成一种视觉假象是图片贴到了圆柱上。

以下是代码:

Shader "Copy/DepthDecal"
{Properties{_MainTex ("Texture", 2D) = "white" {}}SubShader{Tags {"Queue"="Transparent+100"}Pass{Blend SrcAlpha OneMinusSrcAlphaCGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct appdata{float4 vertex : POSITION;};struct v2f{float4 vertex : SV_POSITION;float4 screenPos : TEXCOORD1;float3 ray : TEXCOORD2;};sampler2D _MainTex;sampler2D_float _CameraDepthTexture;v2f vert (appdata v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);o.screenPos = ComputeScreenPos(o.vertex);o.ray =UnityObjectToViewPos(v.vertex)*float3(-1,-1,1);return o;}fixed4 frag (v2f i) : SV_Target{//深度重建视空间坐标float2 screenuv = i.screenPos.xy / i.screenPos.w;//透视除法float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, screenuv);//内置宏对深度纹理采样//当通过纹理采样SAMPLE_DEPTH_TEXTURE之后,得到的深度值往往是非线性的。float viewDepth = Linear01Depth(depth) * _ProjectionParams.z;//乘以远裁剪平面,恢复原来的值//Linear01Depth返回一个范围在【0,1】的线性深度值float3 viewPos = i.ray* (viewDepth / i.ray.z);//转化到世界空间坐标float4 worldPos = mul(unity_CameraToWorld, float4(viewPos, 1.0));//转化为物体空间坐标float3 objectPos = mul(unity_WorldToObject, worldPos);//剔除掉在立方体外面的内容clip(float3(0.5, 0.5, 0.5) - abs(objectPos)); //abs计算输入值的绝对值。//使用物体空间坐标的xz坐标作为采样uvfloat2 uv = objectPos.xz+0.5;fixed4 col = tex2D(_MainTex, uv);return col;}ENDCG}}
}
ComputeScreenPos(o.vertex); 该函数输入的是一个其次裁剪空间坐标,返回的是归一化的标准坐标(NDC),但是切记
这个坐标没有经经过透视除法,所以在片元着色器中才有了这段代码:
float2 screenuv = i.screenPos.xy / i.screenPos.w;下面的这段代码是我唯一没有搞懂的地方:
o.ray =UnityObjectToViewPos(v.vertex)*float3(-1,-1,1);为什么要乘以float3(-1,-1,1)?按字面理解是将x、y轴的方向翻过来,但是为什么?开始我想的是unity的视角空间是
右手坐标系然后我把float3(-1,-1,1)换成float3(1,1,-1)也没有问题,但是当我在片元着色器这样做时渲染效果却不
正确
float3 viewPos = i.ray*float3(1,1,-1)* (viewDepth / i.ray.z);
但是这样却是正确的
float3 viewPos = i.ray*float3(-1,-1,1)* (viewDepth / i.ray.z);
我就百思不得其解,果然还是太菜了吗......float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, screenuv);
当通过纹理采样SAMPLE_DEPTH_TEXTURE之后,得到的深度值往往是非线性的。为什么说是往往?我也不知道,我只知道
深度缓冲的值是非线性的。所以我们要用Linear01Depth(depth)获取线性的深度值,方便我们计算。
float viewDepth = Linear01Depth(depth) * _ProjectionParams.z;
这样行代码是通过线性的深度值还原视角空间未进行过处理的z坐标有关_ProjectionParams
//x = 1,如果投影翻转则x = -1
//y是camera近裁剪平面
//z是camera远裁剪平面
//w是1/远裁剪平面
//Linear01Depth返回一个范围在【0,1】的线性深度值float3 viewPos = i.ray* (viewDepth / i.ray.z);
这行代码是整个shader的精髓,也是关键,我们通过三角形相似的原理,用正方体上的片元视角空间坐标反求圆柱的视角空
间片元坐标,随后我们进行了两次坐标系变换(以判断圆柱是否在正方体内),不得不说虽然代码简单效果也不错,但是在片
元着色器里进行两次矩阵运算对性能是一个较大的损耗。float2 uv = objectPos.xz+0.5;
xz范围在[-0.5,0.5],所以要加0.5。

关于为什么深度缓冲为什么是非线性的,同这也是一个非常好的学习OpenGL的网站深度测试

接下来是两个优化效果,都是针对祖传贴图拉长的。
警告:该优化只在game窗口有正确的渲染效果,scene窗口十分鬼畜,且都需要DepthTextureMode.DepthNormals!
以下是添加到摄像机的脚本:

using UnityEngine;[ExecuteInEditMode]
public class CameraDepthNormalHelper : MonoBehaviour {public DepthTextureMode depthMode = DepthTextureMode.DepthNormals;void OnEnable(){GetComponent<Camera>().depthTextureMode = depthMode;}void OnDisable(){GetComponent<Camera>().depthTextureMode = DepthTextureMode.None;}
}

第一:根据原文的指导思想,不对的就不要渲染出来,好主意Ψ( ̄∀ ̄)Ψ!!
思路就是获取视角空间下的法线坐标,通过与转换到视角空间下的模型空间的y轴进行点乘,把大于等于90度的片元统统丢掉。
以下是效果图:
Scene窗口:

Game窗口:

接下来是代码:

Shader "NewStart/DepthDecalClip"
{Properties{_MainTex ("Texture", 2D) = "white" {}_Threshold("Threshold",Range(0,0.3))=0.1}SubShader{Tags {"Queue"="Transparent+100"}Pass{Blend SrcAlpha OneMinusSrcAlphaCGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct appdata{float4 vertex : POSITION;};struct v2f{float4 vertex : SV_POSITION;float4 screenPos : TEXCOORD1;float3 ray : TEXCOORD2;float3 yDir:TEXCOORD3;};sampler2D _MainTex;sampler2D_float _CameraDepthNormalsTexture;float _Threshold;v2f vert (appdata v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);o.screenPos = ComputeScreenPos(o.vertex);//该函数返回的是齐次坐标下的未经过透视除法的屏幕坐标值o.ray =UnityObjectToViewPos(v.vertex)*float3(-1,-1,1);//将一个点从object空间转换为view空间。o.yDir=UnityObjectToViewPos(float3(0,1,0));//将模型的Y轴转换到视角空间return o;}fixed4 frag (v2f i) : SV_Target{//深度重建视空间坐标float2 screenuv = i.screenPos.xy / i.screenPos.w;//透视除法float depth;float3 viewNormal;// 返回一个视空间深度值 和 一个视空间法线 //// 该深度值是一个线性深度值, 范围是0到1, 精度小于使用SAMPLE_DEPTH_TEXTURE方法获得的深度值 //// 因为DecodeDepthNormal方法中的深度值用16位存储,而SAMPLE_DEPTH_TEXTURE中的深度值用32位存储 //float4 cdn=tex2D(_CameraDepthNormalsTexture,screenuv);DecodeDepthNormal(cdn,depth,viewNormal);float viewDepth = depth * _ProjectionParams.z;//乘以远裁剪平面,恢复原来的值float3 viewPos = i.ray* (viewDepth / i.ray.z);//转化到世界空间坐标float4 worldPos = mul(unity_CameraToWorld, float4(viewPos, 1.0));//转化为物体空间坐标float3 objectPos = mul(unity_WorldToObject, worldPos);//剔除掉在立方体外面的内容clip(float3(0.5, 0.5, 0.5) - abs(objectPos)); //abs计算输入值的绝对值。viewNormal=normalize(viewNormal);float3 yDir=normalize(i.yDir);clip(dot(yDir,viewNormal)-_Threshold);//使用物体空间坐标的xz坐标作为采样uvfloat2 uv = objectPos.xz+0.5;fixed4 col = tex2D(_MainTex, uv);return col;}ENDCG}}
}

代码没什么可说的,无非就是采样的纹理对象不一样了,并且多了一个法线,看得懂第一个这个肯定也没问题

第二:对y轴的的影响也考虑进UV采样中,参考Unity Decal 贴花效果测试
效果:
Scene:

Game:

Shader "NewStart/DepthDecalClip"
{Properties{_MainTex ("Texture", 2D) = "white" {}_Threshold("Threshold",Range(0,0.3))=0.1}SubShader{Tags {"Queue"="Transparent+100"}Pass{Blend SrcAlpha OneMinusSrcAlphaCGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct appdata{float4 vertex : POSITION;};struct v2f{float4 vertex : SV_POSITION;float4 screenPos : TEXCOORD1;float3 ray : TEXCOORD2;float3 yDir:TEXCOORD3;};sampler2D _MainTex;sampler2D_float _CameraDepthNormalsTexture;float _Threshold;v2f vert (appdata v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);o.screenPos = ComputeScreenPos(o.vertex);//该函数返回的是齐次坐标下的未经过透视除法的屏幕坐标值o.ray =UnityObjectToViewPos(v.vertex)*float3(-1,-1,1);//将一个点从object空间转换为view空间。o.yDir=UnityObjectToViewPos(float3(0,1,0));return o;}fixed4 frag (v2f i) : SV_Target{//深度重建视空间坐标float2 screenuv = i.screenPos.xy / i.screenPos.w;//透视除法float depth;float3 viewNormal;// 返回一个视空间深度值 和 一个视空间法线 //// 该深度值是一个线性深度值, 范围是0到1, 精度小于使用SAMPLE_DEPTH_TEXTURE方法获得的深度值 //// 因为DecodeDepthNormal方法中的深度值用16位存储,而SAMPLE_DEPTH_TEXTURE中的深度值用32位存储 //float4 cdn=tex2D(_CameraDepthNormalsTexture,screenuv);DecodeDepthNormal(cdn,depth,viewNormal);float viewDepth = depth * _ProjectionParams.z;//乘以远裁剪平面,恢复原来的值float3 viewPos = i.ray* (viewDepth / i.ray.z);//转化到世界空间坐标float4 worldPos = mul(unity_CameraToWorld, float4(viewPos, 1.0));//转化为物体空间坐标float3 objectPos = mul(unity_WorldToObject, worldPos);//剔除掉在立方体外面的内容clip(float3(0.5, 0.5, 0.5) - abs(objectPos)); //abs计算输入值的绝对值。viewNormal=normalize(viewNormal);float3 yDir=normalize(i.yDir);float nodt=dot(yDir,viewNormal);float offset=1-nodt;float2 uv = objectPos.xz+0.5+offset*float2(0,objectPos.y);fixed4 col = tex2D(_MainTex, uv);return col;}ENDCG}}
}

好了,文章到此结束,感谢你的阅读,如有错误欢迎指正。

Unity Shader 实现简单的贴花效果(二)相关推荐

  1. Unity Shader 之 简单 护盾Shield 效果的实现

    Unity Shader 之 简单 护盾Shield 效果的实现 目录 Unity Shader 之 简单 护盾Shield 效果的实现 一.简单介绍 二.实现原理

  2. Unity Shader 之 简单实现折叠平面(翻书)的效果

    Unity Shader 之 简单实现折叠平面(翻书)的效果 目录 Unity Shader 之 简单实现折叠平面(翻书)的效果 一.简单介绍 二.实现原理 三.注意事项 四.效果预览 五.实现步骤 ...

  3. Unity Shader 之 简单实现物体被压扁(top顶点的逐渐与bottom顶点重合)的效果

    Unity Shader 之 简单实现物体被压扁(top顶点的逐渐与bottom顶点重合)的效果 目录 Unity

  4. Unity Shader 之 简单实现物体被黑洞吸收吞噬(或者从黑洞中出来)的效果

    Unity Shader 之 简单实现物体被黑洞吸收吞噬(或者从黑洞中出来)的效果 目录

  5. Unity Shader学习:Dissolve消融效果

    Unity Shader学习:Dissolve消融效果 消融效果在游戏里非常常用,这里简单的实现下,代码里用到了if分支在shader里可能会费一点,如果想直接用puppet_master大佬版本的话 ...

  6. Unity Shader 实现简单的压扁效果

    有点累啊,一个CoverMap搞了一周多,还是太嫩了,还有好多东西等着我去学呢,今天就写个简单的东西吧--一个把模型压扁的效果,参考博客Unity Shader - 一些玩具Shader.话不多说,先 ...

  7. 【Unity Shader】聚光灯体积光效果的简单实现

    效果如下: Unity中的聚光灯SpotLight,可以用作手电筒,射灯等类似的效果,比如这样的 但是如果想把光束的效果做出来,就超出了SpotLight的能力范围了,本篇就为了记录一下一种简单的实现 ...

  8. Unity Shader - 实现简单水体 - 浅水到深水颜色控制

    文章目录 制作步骤 准备好水体网格 扰动水体网格 添加水体网格色调,纹理 放置海上放哨点(一些随便放的立方体) 添加水的深浅透视效果 添加水光效 重构水顶点法线 正交的相机的深度需要注意 改进 Pro ...

  9. Unity shader学习之屏幕后期处理效果之高斯模糊

    高斯模糊,见 百度百科. 也使用卷积来实现,每个卷积元素的公式为: 其中б是标准方差,一般取值为1. x和y分别对应当前位置到卷积中心的整数距离. 由于需要对高斯核中的权重进行归一化,即使所有权重相加 ...

最新文章

  1. 微信小程序架构分析 (上)
  2. 一年只有0.001「薇」!杜克大学陈怡然教授自嘲「科学家不如带货」
  3. 计算机二级的考试c模板,2013年9月全国计算机等级《二级C++》上机模考试卷(5)
  4. 使用Xshell密钥认证机制远程登录Linux
  5. html img 坐标,Html img 标签
  6. Pycharm不能用了
  7. 旋转矩阵、旋转向量(轴角)、四元数、欧拉角之间相互转换的代码实现(利用Eigen实现)...
  8. hc06蓝牙模块介绍_微测评 | 小米智能插座蓝牙网关版
  9. 微信视频号认证有什么要求?
  10. 时装连连看,基于MindSpore实现FashionMNIST图像分类
  11. linux转换flv文件格式,在linux 如何播放FLV 和 WMV 格式的文件?
  12. 视觉测量系统实现尺寸测量的4个步骤
  13. Windows登录多微信
  14. 游戏中找CALL的万能方法
  15. 线性回归、逻辑回归学习笔记
  16. 逍遥安卓 出现android,解决逍遥安卓模拟器一直卡在99%的方法
  17. 用程序编写计算公式的高次方程数字计算机
  18. 【数据库】数据分析专项练习题库-SQL试卷一
  19. No module named '_bz2'
  20. selenium 安装教程

热门文章

  1. 增长量计算n+1原则_国家公务员考试:资料分析中增长量的计算 (1)
  2. 姑娘,你为什么要学编程?是抖音不好玩还是王者太坑?
  3. java launcher在哪_HomeLauncher启动
  4. python imshow参数_用matplotlib中imshow()函数绘图
  5. 800行代码实现春节倒计时与烟花祝福
  6. Python花呗分析模型温馨提醒:支出不规范,收入两行泪
  7. WordPress主题集成DPlayer播放M3U8流媒体
  8. 批量修改文件后缀(无后缀有后缀的原文件均可)
  9. Word文档被误覆盖后如何恢复?
  10. 小龙虾炒菜机器人_全球首个机器人餐厅开业,机器人炒菜你见过吗?