gpt4 book ai didi

unity3d - 如何在后处理顶点和片段着色器中计算对象的径向距离

转载 作者:行者123 更新时间:2023-12-01 22:49:11 25 4
gpt4 key购买 nike

经过几个小时的谷歌、复制粘贴代码和尝试,我仍然找不到解决问题的方法。

我尝试使用顶点和片段函数编写后处理着色器。我的问题是我不知道如何计算当前顶点到世界坐标中相机位置(或任何其他给定位置)的径向距离。

我的目标如下:

考虑一个非常大的 3D 平面,其中相机位于顶部并且正好向下看向平面。我现在想要一个后处理着色器,它在平面上绘制一条白线,这样只有那些与相机有一定径向距离的像素才被涂成白色。预期结果将是一个白色圆圈(在此特定设置中)。

原则上我知道如何做到这一点,但问题是我无法找到如何计算到顶点的径向距离。

这里的问题可能是这是一个后处理着色器。所以这个shader并没有应用到某个物体上。如果我这样做,我可以使用 mul(unity_ObjectToWorld, v.vertex) 获取顶点的世界坐标,但对于后处理着色器,这给出了一个无意义的值。

这是我针对这个问题的调试代码:

Shader "NonHidden/TestShader"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="Transparent" "Queue"="Transparent-1"}
LOD 100

ZWrite Off

Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 3.0
#include "UnityCG.cginc"

sampler2D _MainTex;
sampler2D _CameraDepthTexture;
uniform float4 _MainTex_TexelSize;

// V2F
struct v2f {
float4 outpos : SV_POSITION;
float4 worldPos : TEXCOORD0;
float3 rayDir : TEXCOORD1;
float3 camNormal : TEXCOORD2;
};


// Sample Depth
float sampleDepth(float2 uv) {
return Linear01Depth(
UNITY_SAMPLE_DEPTH(
tex2D(_CameraDepthTexture, uv)));
}


// VERTEX
v2f vert (appdata_tan v)
{
TANGENT_SPACE_ROTATION;

v2f o;
o.outpos = UnityObjectToClipPos(v.vertex);
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
o.rayDir = mul(rotation, ObjSpaceViewDir(v.vertex));
o.camNormal = UNITY_MATRIX_IT_MV[2].xyz;
return o;
}

// FRAGMENT
fixed4 frag (v2f IN) : SV_Target
{
// Get uv coordinates
float2 uv = IN.outpos.xy * (_ScreenParams.zw - 1.0f);

// Flip y if necessary
#if UNITY_UV_STARTS_AT_TOP
if (_MainTex_TexelSize.y < 0)
{
uv.y = 1 - uv.y;
}
#endif

// Get depth
float depth = sampleDepth(uv);

// Set color
fixed4 color = 0;
if(depth.x < 1)
{
color.r = IN.worldPos.x;
color.g = IN.worldPos.y;
color.b = IN.worldPos.z;
}

return color;
}
ENDCG
}
}
}

当前状态

这张图片显示了相机俯视飞机时的结果: Image 1: Actual result每个像素的蓝色值(无论出于何种原因)为 25。红色和绿色区域反射(reflect)了屏幕的 x-y 坐标。

即使稍微旋转相机,我也会在相同的屏幕坐标下得到完全相同的阴影: Image 2: Actual result with rotated camera

这表明计算出的“worldPos”坐标是屏幕坐标,与平面的世界坐标无关。

预期结果

我希望看到的结果如下:

Image 3: Expected result

在这里,与相机具有相同(径向)距离的像素具有相同的颜色。

我需要怎样修改上面的代码才能达到这个效果呢?使用 rayDir(在 vert 函数中计算),我尝试至少获取从相机中心到当前像素的方向向量,这样我就可以使用深度信息计算径向距离。但是 rayDir 对所有像素都有一个恒定值 ...

在这一点上,我还不得不说,我真的不明白 vert 函数内部计算的是什么。这只是我在互联网上找到并尝试过的东西。

最佳答案

好吧,我找到了解决问题的方法,因为我在这里找到了这个视频:Shaders Case Study - No Man's Sky: Topographic Scanner

在视频描述中有一个指向相应 GIT 存储库的链接。我下载、分析并重写了代码,使其符合我的目的,更易于阅读和理解。

我了解到的主要事情是,没有使用后处理着色器计算径向距离的内置方法(如果我错了请纠正我!)。因此,为了获得径向距离,实际上唯一的方法似乎是使用从相机到顶点和深度缓冲区的方向向量。由于方向向量也无法以内置方式获得,因此使用了一个技巧:

除了在后处理脚本中使用 Graphics.Blit 函数,还可以使用自定义 Blit 函数来设置一些额外的着色器变量。在这种情况下,相机的截锥体存储在第二组纹理坐标中,然后在着色器代码中作为 TEXCOORD1 使用。这里的技巧是相应的着色器变量自动包含一个插值 uv 值,它与我正在寻找的方向向量(“frustum ray”)相同。

现在调用脚本的代码如下所示:

using UnityEngine;
using System.Collections;

[ExecuteInEditMode]
public class TestShaderEffect : MonoBehaviour
{

private Material material;
private Camera cam;

void OnEnable()
{
// Create a material that uses the desired shader
material = new Material(Shader.Find("Test/RadialDistance"));

// Get the camera object (this script must be assigned to a camera)
cam = GetComponent<Camera>();

// Enable depth buffer generation#
// (writes to the '_CameraDepthTexture' variable in the shader)
cam.depthTextureMode = DepthTextureMode.Depth;
}

[ImageEffectOpaque] // Draw after opaque, but before transparent geometry
void OnRenderImage(RenderTexture source, RenderTexture destination)
{
// Call custom Blit function
// (usually Graphics.Blit is used)
RaycastCornerBlit(source, destination, material);
}


void RaycastCornerBlit(RenderTexture source, RenderTexture destination, Material mat)
{

// Compute (half) camera frustum size (at distance 1.0)
float angleFOVHalf = cam.fieldOfView / 2 * Mathf.Deg2Rad;
float heightHalf = Mathf.Tan(angleFOVHalf);
float widthHalf = heightHalf * cam.aspect; // aspect = width/height

// Compute helper vectors (camera orientation weighted with frustum size)
Vector3 vRight = cam.transform.right * widthHalf;
Vector3 vUp = cam.transform.up * heightHalf;
Vector3 vFwd = cam.transform.forward;


// Custom Blit
// ===========

// Set the given destination texture as the active render texture
RenderTexture.active = destination;

// Set the '_MainTex' variable to the texture given by 'source'
mat.SetTexture("_MainTex", source);

// Store current transformation matrix
GL.PushMatrix();

// Load orthographic transformation matrix
// (sets viewing frustum from [0,0,-1] to [1,1,100])
GL.LoadOrtho();

// Use the first pass of the shader for rendering
mat.SetPass(0);

// Activate quad draw mode and draw a quad
GL.Begin(GL.QUADS);
{

// Using MultiTexCoord2 (TEXCOORD0) and Vertex3 (POSITION) to draw on the whole screen
// Using MultiTexCoord to write the frustum information into TEXCOORD1
// -> When the shader is called, the TEXCOORD1 value is automatically an interpolated value

// Bottom Left
GL.MultiTexCoord2(0, 0, 0);
GL.MultiTexCoord(1, (vFwd - vRight - vUp) * cam.farClipPlane);
GL.Vertex3(0, 0, 0);

// Bottom Right
GL.MultiTexCoord2(0, 1, 0);
GL.MultiTexCoord(1, (vFwd + vRight - vUp) * cam.farClipPlane);
GL.Vertex3(1, 0, 0);

// Top Right
GL.MultiTexCoord2(0, 1, 1);
GL.MultiTexCoord(1, (vFwd + vRight + vUp) * cam.farClipPlane);
GL.Vertex3(1, 1, 0);

// Top Left
GL.MultiTexCoord2(0, 0, 1);
GL.MultiTexCoord(1, (vFwd - vRight + vUp) * cam.farClipPlane);
GL.Vertex3(0, 1, 0);

}
GL.End(); // Finish quad drawing

// Restore original transformation matrix
GL.PopMatrix();
}
}

着色器代码如下所示:

Shader "Test/RadialDistance"
{
Properties
{
_MainTex("Texture", 2D) = "white" {}
}
SubShader
{
// No culling or depth
Cull Off ZWrite Off ZTest Always

Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag

#include "UnityCG.cginc"

struct VertIn
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float4 ray : TEXCOORD1;
};

struct VertOut
{
float4 vertex : SV_POSITION;
float2 uv : TEXCOORD0;
float4 interpolatedRay : TEXCOORD1;
};


// Parameter variables
sampler2D _MainTex;

// Auto filled variables
float4 _MainTex_TexelSize;
sampler2D _CameraDepthTexture;


// Generate jet-color-sheme color based on a value t in [0, 1]
half3 JetColor(half t)
{
half3 color = 0;
color.r = min(1, max(0, 4 * t - 2));
color.g = min(1, max(0, -abs( 4 * t - 2) + 2));
color.b = min(1, max(0, -4 * t + 2));
return color;
}


// VERT
VertOut vert(VertIn v)
{
VertOut o;

// Get vertex and uv coordinates
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv.xy;

// Flip uv's if necessary
#if UNITY_UV_STARTS_AT_TOP
if (_MainTex_TexelSize.y < 0)
o.uv.y = 1 - o.uv.y;
#endif

// Get the interpolated frustum ray
// (generated the calling script custom Blit function)
o.interpolatedRay = v.ray;

return o;
}


// FRAG
float4 frag (VertOut i) : SV_Target
{
// Get the color from the texture
half4 colTex = tex2D(_MainTex, i.uv);

// flat depth value with high precision nearby and bad precision far away???
float rawDepth = DecodeFloatRG(tex2D(_CameraDepthTexture, i.uv));

// flat depth but with higher precision far away and lower precision nearby???
float linearDepth = Linear01Depth(rawDepth);

// Vector from camera position to the vertex in world space
float4 wsDir = linearDepth * i.interpolatedRay;

// Position of the vertex in world space
float3 wsPos = _WorldSpaceCameraPos + wsDir;

// Distance to a given point in world space coordinates
// (in this case the camera position, so: dist = length(wsDir))
float dist = distance(wsPos, _WorldSpaceCameraPos);

// Get color by distance (same distance means same color)
half4 color = 1;
half t = saturate(dist/100.0);
color.rgb = JetColor(t);

// Set color to red at a hard-coded distance -> red circle
if (dist < 50 && dist > 50 - 1 && linearDepth < 1)
{
color.rgb = half3(1, 0, 0);
}

return color * colTex;
}
ENDCG
}
}
}

我现在能够达到预期的效果:

Image

但是我还有一些问题,如果有人能为我解答,我将不胜感激:

  • 真的没有其他方法可以获取径向距离吗?使用方向向量和深度缓冲区效率低且不准确
  • 我不太明白 rawDepth 变量的内容。我的意思是,是的,它是一些深度信息,但是如果您使用深度信息作为纹理颜色,那么如果您离某个物体不是特别近的话,您基本上会得到一个黑色图像。这会导致距离较远的物体的分辨率非常差。任何人都可以使用它吗?
  • 我不明白 Linear01Depth 函数到底做了什么。由于 Unity 文档总体上很糟糕,因此它也不提供有关此文档的任何信息

关于unity3d - 如何在后处理顶点和片段着色器中计算对象的径向距离,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48023079/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com