UnityShader-深度纹理(理解以及遇到的问题)

news/2024/7/24 18:43:32 标签: unity, c#, 游戏引擎

1. 获得深度纹理

unity中获取深度纹理是比较简单的,通过将脚本挂在摄像机上,代码如下,参考自UnityShader入门精要.

用于生成材质的一个基类:

using UnityEngine;
using System.Collections;

[ExecuteInEditMode]
[RequireComponent (typeof(Camera))]
public class PostEffectsBase : MonoBehaviour {

	// Called when start
	protected void CheckResources() {
		bool isSupported = CheckSupport();
		
		if (isSupported == false) {
			NotSupported();
		}
	}

    // Called in CheckResources to check support on this platform
    [System.Obsolete]
    protected bool CheckSupport() {
		if (SystemInfo.supportsImageEffects == false || SystemInfo.supportsRenderTextures == false) {
			Debug.LogWarning("This platform does not support image effects or render textures.");
			return false;
		}
		
		return true;
	}

	// Called when the platform doesn't support this effect
	protected void NotSupported() {
		enabled = false;
	}
	
	protected void Start() {
		CheckResources();
	}
	
	// Called when need to create the material used by this effect
	protected Material CheckShaderAndCreateMaterial(Shader shader, Material material) {
		if (shader == null) {
			return null;
		}
		
		if (shader.isSupported && material && material.shader == shader)
			return material;
		
		if (!shader.isSupported) {
			return null;
		}
		else {
			material = new Material(shader);
			material.hideFlags = HideFlags.DontSave;
			if (material)
				return material;
			else 
				return null;
		}
	}
}

获得深度值(虽然名字取的获得深度值和法线值,但是还没有获得后者)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 获得深度和法线纹理
/// </summary>
public class GetDepthAndNormalTexture : PostEffectsBase
{
	// 需要挂载的Shader
	public Shader depthShader;
	// 自动生成的材质
	private Material depthMaterial = null;

	public Material material
	{
		get
		{
			depthMaterial = CheckShaderAndCreateMaterial(depthShader, depthMaterial);
			return depthMaterial;
		}
	}
	private Camera myCamera;
	public Camera camera
	{
		get
		{
			if (myCamera == null)
			{
				myCamera = GetComponent<Camera>();
			}
			return myCamera;
		}
	}

	void OnEnable()
	{
		camera.depthTextureMode |= DepthTextureMode.Depth;
	}
	void OnRenderImage(RenderTexture src, RenderTexture dest)
	{
		if (material != null)
		{
			Graphics.Blit(src, dest, material);
			//Matrix4x4 currentViewProjectionMatrix = camera.projectionMatrix * camera.worldToCameraMatrix;
			//Matrix4x4 currentViewProjectionInverseMatrix = currentViewProjectionMatrix.inverse;
			//material.SetMatrix("_CurrentViewProjectionInverseMatrix", currentViewProjectionInverseMatrix);
			Matrix4x4 projection = GL.GetGPUProjectionMatrix(camera.projectionMatrix, false) * camera.worldToCameraMatrix;
			material.SetMatrix("_CurrentViewProjectionInverseMatrix", projection.inverse);
		}
		else
		{
			Graphics.Blit(src, dest);
		}
	}
}

2. 注意事项

2.1 首先需要明确一点,通过下面这句代码,我们可以在Shader中声明 _CameraDepthTexture 变量来获得深度纹理,这里的深度存储的是 NDC 坐标,且将范围映射到了 [ 0 , 1 ] [0, 1] [0,1].

camera.depthTextureMode |= DepthTextureMode.Depth;

2.2 如何通过Shader可视化深度图呢?
先把代码贴出来:

Shader "Unity Shaders Book/Chapter 13/DepthAndNormal"
{
    Properties
    {
        _MainTex ("Base (RGB)", 2D) = "white" {}
    }
    SubShader
    {
        CGINCLUDE
		#include "UnityCG.cginc"
        sampler2D _MainTex;
        half4 _MainTex_TexelSize;
        sampler2D _CameraDepthTexture;
        float4x4 _CurrentViewProjectionInverseMatrix;

        struct v2f{
            float4 pos:SV_POSITION;
            half2 uv:TEXCOORD0;
            half2 uv_depth:TEXCOORD1;
        };
        

        v2f vert(appdata_img v){
            v2f o;
            o.pos = UnityObjectToClipPos(v.vertex);
            o.uv = v.texcoord;
            o.uv_depth = v.texcoord;
            #if UNITY_UV_STARTS_AT_TOP // 如果uv的y轴是从上面开始的, 1表示Direct3d,0表示Opengl
            if(_MainTex_TexelSize.y < 0)
                o.uv_depth.y = 1 - o.uv_depth.y;
			#endif
            return o;
        }

        fixed4 frag(v2f i) : SV_Target{
            // 采样深度纹理获得非线性空间中的深度值
            float d = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv_depth);
            float4 H = float4(i.uv.x * 2 - 1, i.uv.y * 2 - 1, d, 1);
            // float4 H = float4(i.uv.x * 2 - 1, i.uv.y * 2 - 1, d * 2 - 1, 1);
            float4 D = mul(_CurrentViewProjectionInverseMatrix, H); // 映射回线性空间中去
            float4 worldPos = D / D.w;
            float world_d = saturate(worldPos.z / 96);
            float linear_d = Linear01Depth(d);

            float4 proj = float4(i.uv * 2 - 1, d, 1);
            worldPos = mul(_CurrentViewProjectionInverseMatrix, proj);
            worldPos = worldPos / worldPos.w;
            // return fixed4(linear_d, linear_d, linear_d, 1.0);
            return fixed4(worldPos.z, worldPos.z, worldPos.z, 1.0);
        }

        ENDCG


        Pass {      
			ZTest Always Cull Off ZWrite Off
			    	
			CGPROGRAM  
			
			#pragma vertex vert  
			#pragma fragment frag  
			  
			ENDCG  
		}

    }
    FallBack Off
}

重点看frag函数!!!


如果想获得视角空间下的深度值:
可以通过LinearEyeDepth函数来获得,改动一下返回代码即可:

float view_linear_d = LinearEyeDepth(d);
return fixed4(view_linear_d, view_linear_d, view_linear_d, 1.0);

效果(有点奇怪,感觉还有问题,有大佬欢迎提出一点意见):
在这里插入图片描述


如果想获得一个范围在 [ 0 , 1 ] [0,1] [0,1]的线性深度值,那么可以通过Linear01Depth函数来获得,改动一下返回代码即可:

float linear_d = Linear01Depth(d);
return fixed4(linear_d, linear_d, linear_d, 1.0);

效果:
在这里插入图片描述


如果想要通过深度值来获得世界坐标,那么代码如下:

float d = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv_depth);
float4 H = float4(i.uv.x * 2 - 1, i.uv.y * 2 - 1, d, 1);
// float4 H = float4(i.uv.x * 2 - 1, i.uv.y * 2 - 1, d * 2 - 1, 1);
float4 D = mul(_CurrentViewProjectionInverseMatrix, H); // 映射回线性空间中去
float4 worldPos = D / D.w;
return fixed4(worldPos.z, worldPos.z, worldPos.z, 1.0);

效果:
在这里插入图片描述

3. 遇到的问题

GL.GetGPUProjectionMatrix(camera.projectionMatrix, false)
通过这个函数可以用来处理DX 和OpenGL的坐标差异性。
之前没有用这个函数,导致坐标系不同,没有产生正确的效果。
Unity中的编辑界面是左手坐标系,但是底层使用的是右手坐标系,因此如果这里不统一,那么会导致z轴的值有问题。


http://www.niftyadmin.cn/n/1223908.html

相关文章

切线空间以及TBN矩阵

参考书籍&#xff1a;UnityShader入门精要 参考文章&#xff1a;为什么要有切线空间&#xff08;Tangent Space&#xff09;&#xff0c;它的作用是什么&#xff1f; 切线空间 首先需要了解一个前提&#xff0c;在图形学中我们会用到很多的坐标空间&#xff0c;而切线空间只是…

Unity Hub登录无响应

以下是我遇到的问题以及解决方案&#xff0c;在此之前这篇博文说的也很不错&#xff0c;可以参考一下。 Unity Hub 3 登录无响应(无法登录)解决方式 主要是看能不能弹出来登录窗口&#xff0c;找了半天的解决方案&#xff0c;最终发现是默认浏览器的锅。 去设置里改一下web浏…

python 绘制三维向量

简单粗暴&#xff0c;直接丢代码 里面涉及到的函数可以自己去官方文档或者直接看源码。 import matplotlib.pyplot as plt import numpy as np import numpy.randomdef creat3d_vec_xyz(start_points, end_points, colorsNone, view_angle20, azimuth30):创建一个空间三维坐标…

非真实感渲染(NPR)论文理解及其复现(Unity) - 《Stylized Highlights for Cartoon Rendering and Animation》

Stylized Highlights for Cartoon Rendering and Animation 该论文的目的主要是用来渲染卡通风格的高光。复现参考自【NPR】非真实感渲染实验室 符号&#xff1a; ::: 表示定义为 核心思想 整篇论文的主要思想是通过控制半程向量 H\mathbf{H}H 来控制高光的大小、范围、形状…

UnityShader-LowPoly

LowPoly 一种低多边形风格画作或效果。 参考文章&#xff1a; 【Unity Shader】新书封面 — Low Polygon风格的渲染 图形学进阶——曲面细分与几何着色器 效果如下&#xff1a; 原理 就我个人的理解来看&#xff0c;原始的法线如上面的左图&#xff0c;在片元着色器阶段&#…

绕任意轴旋转矩阵推导

该文是在学习 Physically Based Rendering 第2.7.6节绕任意轴旋转时对其公式的推导产生了兴趣。 首先&#xff0c;如图所示&#xff1a; 已知条件&#xff1a; 1). v\mathbf{v}v 是被旋转的向量。 2). a\mathbf{a}a 是围绕旋转的轴。 3). θ\thetaθ 是旋转的角度。 解决思路…

写在前面的话-前言

写在前面的话&#xff1a; 码龄4年&#xff0c;应该是申请注册CSDN账号4年了&#xff0c;期间看过很多文章&#xff0c;关注了一些大牛。 自己一直信奉的宗旨就是“好记性不如烂笔头”&#xff0c;结果的结果记录了一些内容&#xff0c;但是始终形成不了体系&#xff0c;自己也…

基础算法系列 之冒泡排序

冒泡排序是入门算法必学的内容&#xff0c;就像练guitar时的“兰花草”和“恰似你的温柔”一样&#xff0c;都是基础开篇内容。 冒泡排序的准则就是“大数沉淀&#xff0c;小数冒泡&#xff1b;双重循环&#xff0c;两轮控制。”基本代码如下&#xff1a; public static void …