Unity CPU 中使用深度贴图
大多数文章讲的都是在gpu中使用深度贴图 我记忆一下在CPU 中使用深度贴图。
我们今天利用深度贴图计算出世界坐标 。
用深度贴图计算世界坐标
前言
有射线的时候可以直接根据射线求出世界坐标。 如果没有包围盒子就很难根据鼠标输入求出世界坐标
一、把GPU中的深度贴图取出来
参考这个文章
depthRT = new RenderTexture(Width, Height, 16, RenderTextureFormat.Depth);
depthRT.name = "MainDepthBuffer";
///深度值 用 单通道 16bit 存起来
depthTex = new RenderTexture(Width, Height, 0, RenderTextureFormat.R16);
depthTex.name = "SceneDepthTex";
_cbDepth = new CommandBuffer();
_cbDepth.name = "CommandBuffer_DepthBuffer";
///得将深度贴图拷贝到一个 rendertexture 里面
_cbDepth.Blit(depthRT.depthBuffer, depthTex.colorBuffer);
二、将根据鼠标输入取出RenderTexture里面的像素
//将深度rendertexture拷贝到 texture2d
myTexture = new Texture2D(Width, Height, TextureFormat.R16, false);
RenderTexture.active = depthTex;
Vector2 tmpPos = Input.mousePosition;
int posX = Mathf.CeilToInt(tmpPos.x);
int posY = Mathf.CeilToInt(tmpPos.y);
myTexture.ReadPixels(new Rect(0, 0, Screen.width, Screen.height), 0, 0);
myTexture.Apply();
//取出某一个点的像素 r 通道即为 深度值
Color tmpColor = myTexture.GetPixel(posX, posY);
三、求出距离相机的距离
根据这个文章
float CaculateZ( float d)
{
///由于 DX 平台需要取反 有些平台不需要
float result = 1-d;
float tmpMinus = _Camera.nearClipPlane - _Camera.farClipPlane ;
float tmpMuti = _Camera.farClipPlane * _Camera.nearClipPlane;
result = ((tmpMinus * result) / tmpMuti) + (1.0f / _Camera.nearClipPlane);
result = 1.0f / result;
return result;
}
四、算出世界坐标
Vector3 screenPos = Input.mousePosition;
float tmpZ = CaculateZ(tmpColor.r);
screenPos.z = tmpZ ;
Vector3 worldPos= Camera.main.ScreenToWorldPoint(screenPos);
Camera.main.ScreenToWorldPoint 这个输入值 Z 值表示距离 相机的距离 才能得到正确的 世界坐标。
可以使用另外一种方式算出世界坐标:
在相机前面摆一个 平面 ,然后用射线找到输入点的世界坐标
void CaculateScreenDepth(float z)
{
Vector3 tmpZ = new Vector3(0, 0, z + _Camera.transform.position.z);
Plane plane = new Plane(-Vector3.forward, tmpZ);
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
float dist;
if (plane.Raycast(ray, out dist))
{
Vector3 P = ray.GetPoint(dist);
Debug.Log("p==" + P);
GameObject tmpObj = GameObject.CreatePrimitive(PrimitiveType.Sphere);
tmpObj.transform.localScale = Vector3.one * 0.1f;
tmpObj.transform.position = P;
}
}
两种方式等价。
五、全部代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
public class TestDepth : MonoBehaviour
{
private RenderTexture depthRT;
private RenderTexture colorRT;
private RenderTexture depthTex;
private CommandBuffer _cbDepth = null;
private Camera _Camera = null;
public GameObject mainTexture;
private void Awake()
{
_Camera = Camera.main;
int Width = Screen.width;
int Height = Screen.height;
_Camera.depthTextureMode = DepthTextureMode.Depth;
depthRT = new RenderTexture(Width, Height, 16, RenderTextureFormat.Depth);
depthRT.name = "MainDepthBuffer";
colorRT = new RenderTexture(Width, Height, 0, RenderTextureFormat.ARGB32);
colorRT.name = "MainColorBuffer";
depthTex = new RenderTexture(Width, Height, 0, RenderTextureFormat.R16);
depthTex.name = "SceneDepthTex";
_cbDepth = new CommandBuffer();
_cbDepth.name = "CommandBuffer_DepthBuffer";
_cbDepth.Blit(depthRT.depthBuffer, depthTex.colorBuffer);
myTexture = new Texture2D(Width, Height, TextureFormat.R16, false);
// _cbDepth.Blit(depthTex.colorBuffer, myTexture);
_Camera.AddCommandBuffer(CameraEvent.AfterDepthTexture, _cbDepth);
mainTexture.GetComponent<Renderer>().material.mainTexture = myTexture;
}
float CaculateZ( float d)
{
float result = 1-d;
float tmpMinus = _Camera.nearClipPlane - _Camera.farClipPlane ;
float tmpMuti = _Camera.farClipPlane * _Camera.nearClipPlane;
result = ((tmpMinus * result) / tmpMuti) + (1.0f / _Camera.nearClipPlane);
result = 1.0f / result;
return result;
}
Texture2D myTexture;
private void Update()
{
if (Input.GetMouseButtonDown(0))
{
RenderTexture.active = depthTex;
Vector2 tmpPos = Input.mousePosition;
int posX = Mathf.CeilToInt(tmpPos.x);
int posY = Mathf.CeilToInt(tmpPos.y);
myTexture.ReadPixels(new Rect(0, 0, Screen.width, Screen.height), 0, 0);
myTexture.Apply();
Color tmpColor = myTexture.GetPixel(posX, posY);
Vector3 screenPos = Input.mousePosition;
float tmpZ = CaculateZ(tmpColor.r);
screenPos.z = tmpZ ;
Vector3 worldPos= Camera.main.ScreenToWorldPoint(screenPos);
}
}
void OnPreRender()
{
_Camera.SetTargetBuffers(colorRT.colorBuffer, depthRT.depthBuffer);
}
private void OnPostRender()
{
//目前的机制不需要这次拷贝
Graphics.Blit(colorRT, (RenderTexture)null);
}
void CaculateScreenDepth(float z)
{
Vector3 tmpZ = new Vector3(0, 0, z + _Camera.transform.position.z);
Plane plane = new Plane(-Vector3.forward, tmpZ);
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
float dist;
if (plane.Raycast(ray, out dist))
{
Vector3 P = ray.GetPoint(dist);
Debug.Log("p==" + P);
GameObject tmpObj = GameObject.CreatePrimitive(PrimitiveType.Sphere);
tmpObj.transform.localScale = Vector3.one * 0.1f;
tmpObj.transform.position = P;
}
}
}