traceRay函数

在上一篇中,我们有如下签名的traceRay函数

bool traceRay(float3 start, float3 direction, out float2 hitPixel, out float3 debugCol ) {
}

其中的参数意义都很明了。start和direction是相机空间下的光线起点,以及光线方向。

traceRay的核心代码并不复杂,如下:

#define RAY_LENGTH 2.0
#define STEP_COUNT 64 //maximum sample count.
UNITY_LOOP //强制使用循环结构,不然就会代码5秒钟,编译1小时
for (int i = 1; i <= STEP_COUNT; i++) {
float3 p = start + (float)i/STEP_COUNT * RAY_LENGTH * direction ; //p是当前的光线的空间位置
float pDepth = p.z / -_ProjectionParams.z; //_ProjectionParams.z是far clip plane的值。又因为viewspace下正前方z值是负的,所以加个负号。
float4 screenCoord = mul(_Projection, float4(p,1)); //将光线投影到screen space中。
screenCoord /= screenCoord.w;
if (screenCoord.x < -1 || screenCoord.y < -1 || screenCoord.x > 1 || screenCoord.y > 1)
return false;
float camDepth = Linear01Depth(tex2Dlod(_CameraDepthTexture, float4(screenCoord.xy / 2 + 0.5,0,0))); //获取当前像素的深度。为了使用循环结构,这里必须用tex2Dlod而不是tex2D。
if (Intersect(pDepth,camDepth) ) { //相交检测
hitPixel = screenCoord.xy / 2 + 0.5;
debugCol = float3(hitPixel, 0);
return true;
}
}

相交检测

最简单的方式

最简单的,如果该像素的深度大于当前光线的深度(离相机更远),此时我们认为这是一个命中。

if (pDepth > camDepth) {
...
}



该种方法如上图所示,可以看到物体的下方会有明显的“拖影”。

加入厚度

为了改进效果,我们加入一个像素厚度的考量。当光线位于像素后面,并且不超出该像素的厚度时,才算命中。我们往往给像素一个固定的厚度。

if (pDepth > camDepth && pDepth < camDepth + 0.001 ) {        //0.001是厚度
...
}



如图,拖影不见了。

获取像素实际的厚度

这种方法一般情况下就已经足够好了。如果要进一步改进的话,我们可以通过backface渲染,得到第二张深度贴图。通过将两张深度贴图的采样相减,得到一个像素的“厚度”。再按照这个厚度去做相交测试。

后处理脚本:

    private void OnRenderImage(RenderTexture source, RenderTexture destination) {
RenderBackface();
mat.SetTexture("_BackfaceTex", GetBackfaceTexture());
mat.SetMatrix("_WorldToView", GetComponent<Camera>().worldToCameraMatrix);
Graphics.Blit(source, destination, mat,0);
}
private void RenderBackface() {
if (backfaceCamera == null) {
var t = new GameObject();
var mainCamera = Camera.main;
t.transform.SetParent(mainCamera.transform);
t.hideFlags = HideFlags.HideAndDontSave;
backfaceCamera = t.AddComponent<Camera>();
backfaceCamera.CopyFrom(mainCamera);
backfaceCamera.enabled = false;
backfaceCamera.clearFlags = CameraClearFlags.SolidColor;
backfaceCamera.backgroundColor = Color.white;
backfaceCamera.renderingPath = RenderingPath.Forward;
backfaceCamera.SetReplacementShader(backfaceShader, "RenderType");
backfaceCamera.targetTexture = GetBackfaceTexture();
}
backfaceCamera.Render(); } private RenderTexture backfaceText;
private RenderTexture GetBackfaceTexture() {
if (backfaceText == null) {
backfaceText = new RenderTexture(Screen.width, Screen.height, 24, RenderTextureFormat.RFloat);
backfaceText.filterMode = FilterMode.Point; //VERY IMPORTANT!
}
return backfaceText;
}

渲染背面深度的shader(来自kode80):

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "Unlit/BackfaceShader"
{
Properties
{
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Cull Front Pass
{
CGPROGRAM #pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc" struct v2f {
float4 position : POSITION;
float4 linearDepth : TEXCOORD0;
}; v2f vert(appdata_base v) {
v2f output;
output.position = UnityObjectToClipPos(v.vertex);
output.linearDepth = float4(0.0, 0.0, COMPUTE_DEPTH_01, 0.0);
return output;
} float4 frag(v2f input) : COLOR
{
return float4(input.linearDepth.z, 0.0, 0.0, 0.0);
} ENDCG }
}
}
					float camDepth = Linear01Depth(tex2Dlod(_CameraDepthTexture, float4(screenCoord.xy / 2 + 0.5, 0, 0)));
float backZ = tex2Dlod(_BackfaceTex, float4(screenCoord.xy / 2 + 0.5, 0, 0)).r;
if (pDepth > camDepth && pDepth < backZ) {
hitPixel = screenCoord.xy / 2 + 0.5;
debugCol = float3(hitPixel, 0);
return true;
}



如图

注意我在C#脚本中标注的IMPORTANT一行。少了这一行导致了一个非常难debug的bug。具体原因是相机的深度贴图是Point filter的,而自己创建的rendertexture是默认Bilinear filter的;如果不修改的话,我们用同一个坐标去采样会导致实际上是不同位置的采样进行相减。

要注意的是,这种获取物体厚度的办法并不万能。比如一个物体是只有单面的,此时厚度计算就会出问题(可以想想为什么),类似的,如果相机在一个物体内部(其实也相当于单面)也会出问题。

对于这些单面物体,如果是透明物体,可以设置为Transparent,不写入z缓冲,并且RenderType设置为非Opqaue,此时背面渲染shader就会忽视这个物体。

同时,此时光线有可能和物体的“背面“相交,但是毫无疑问我们只能获得物体“正面”的颜色信息。此时反射出现的内容依然是物体的正面,对于纯色物体这没什么问题,但是对于其他物体就会显得很weird了。

在Unity中实现屏幕空间反射Screen Space Reflection(2)的更多相关文章

  1. 在Unity中实现屏幕空间反射Screen Space Reflection(4)

    第四部分讲一下如何在2D屏幕空间步进光线. http://casual-effects.blogspot.com/2014/08/screen-space-ray-tracing.html 中的代码感 ...

  2. 在Unity中实现屏幕空间反射Screen Space Reflection(1)

    本篇文章我会介绍一下我自己在Unity中实现的SSR效果 出发点是理解SSR效果的原理,因此最终效果不是非常完美的(代码都是够用就行),但是从学习的角度来说足以学习到SSR中的核心算法. 如果对核心算 ...

  3. 在Unity中实现屏幕空间反射Screen Space Reflection(3)

    本篇讲一下相交检测的优化.有两个措施. 线段相交检测 之前的检测都是检测光线的终点是否在物体内.我们可以尝试检测光线的线段是否与物体相交. 比如说有一个非常薄的物体,光线差不多垂直于它的表面.如果用普 ...

  4. 高级屏幕空间反射: Screen Space Reflection (SSSR)

    SSSR进一步调优,对标寒霜级技术水平,实现方式为Direct3D 11+自主实现实时渲染引擎,方法为对比测试.实现已经有段时间了,还是简要更新下吧.以下画面中的SSSR效果全部采用1:4 resol ...

  5. 高级屏幕空间反射: Screen Space Reflection (SSR)

    自从CE3首倡SSR以来,发展至今,其质量与当年早已不能同日而语.不仅强调超越性的质量,而且强调超越性的性能.乘着周末有空撸了撸,以下是增强型实时SSR结果图.与我原来的SSR原始实现相比,新的增强型 ...

  6. screen space reflection/soft alpha test/

    http://www.crytek.com/cryengine/presentations/secrets-of-cryengine-3-graphics-technology 很多宝贝里面 不止题目 ...

  7. 在Unity中实现屏幕空间阴影(1)

    接着上篇文章,我们实现了SSR效果. 其中的在屏幕空间进行光线追踪的方法是通用的.借此我们再实现一种屏幕空间的效果,即屏幕空间阴影. 文中的图片来自Catlike coding http://catl ...

  8. 在Unity中实现屏幕空间阴影(2)

    参考文章: https://www.imgtec.com/blog/implementing-fast-ray-traced-soft-shadows-in-a-game-engine/ 完成的工程: ...

  9. 关于Unity中的屏幕适配

    一.Game视图的屏幕分辨率可以先自定义添加,供以后选择,以下是手游经常用到的分辨率: 1.1136X640,iPhone5 2.1920X1080,横屏,主流游戏都是这个分辨率 3.1080X192 ...

随机推荐

  1. 201621123037《Java程序设计》第二周学习总结

    #Week02-Java基本语法与类库 1. 本周学习总结 关键词:常量池.对象.null.不可变性.string对象拼接.字符串池 关键概念之间的联系:Java中有常量池,超出常量池以外的就会新开辟 ...

  2. 敏捷冲刺DAY4

    一. 每日会议 1. 照片 2. 昨日完成工作 登录界面的进一步完善 服务器搭建 建立数据库 3. 今日完成工作 发布和提供需求功能的实现 用户修改自己的信息 用户界面设计 管理员界面设计 4. 工作 ...

  3. 树莓派无显示器、无网线,优盘(U盘)启动,远程桌面

    版权声明:若无来源注明,Techie亮博客文章均为原创. 转载请以链接形式标明本文标题和地址: 本文标题:树莓派无显示器.无网线,优盘(U盘)启动,远程桌面     本文地址:http://techi ...

  4. java 使用volatile实现线程数据的共享

    java 使用volatile实现线程数据的共享 直接上代码看效果: public class VolatileTest extends Thread { private volatile boole ...

  5. Linux下启用MySQL慢查询

    MySQL在linux系统中的配置文件一般是my.cnf找到[mysqld]下面加上log-slow-queries=/data/mysqldata/slowquery.loglong_query_t ...

  6. 【loj6342】跳一跳 期望dp

    题目描述 一个人从 $1$ 开始向 $n$ 跳,在 $i$ 时会等概率跳到 $i,i+1,...,n$ 之一.求从 $1$ 跳到 $n$ 的期望步数. $n\le 10^7$ . 题解 期望dp傻逼题 ...

  7. Going in Cycle!! UVA - 11090(二分+判断环路 )

    题意: 给定一个n个点m条边的加权有向图,求平均权值最小的回路 解析: 首先肯定是想到找出环路  然后..呵..呵..呵呵... 显然不现实!! 二分大法好 ....去猜结果 然后带入验证 ...真是 ...

  8. Bootstrap 环境安装

    下载 Bootstrap 可以从 http://getbootstrap.com/ 上下载 Bootstrap 的最新版本.当点击这个链接时,将看到如下所示的网页: 您会看到两个按钮: Downloa ...

  9. [Oracle整理]ORA-12705(字符集问题)

    [Oracle整理]ORA-12705(字符集问题)   2017年5月11日 18:11 [Oracle整理]ORA-12705(字符集问题) 说明:本内容是工作用到的知识点整理,来自工作中和网络. ...

  10. [CF1087D]Minimum Diameter Tree

    link 题目大意 有$n$个点的前边权为$0$的树,你要加入$S$边权总量,可以为分数,使得当前树的直径最小. 题目分析 题目过于毒瘤,导致于最后$1$个小时一直在做此题,没想到真的只是一个结论一样 ...