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. scrum站立会议介绍

    什么是站立会议? 站立会议是敏捷软件开发方法论Scrum的相关技术之一,亦可称之为Scrum的最佳实践.具体形式为每天的同一时间,一个敏捷开发团队的所有成员面对面站在一起,进行一个为期15~20分钟的 ...

  2. mybatis_mysql

    SELECT round(avg(c.AVG_DELAY_TIME)) as AVG_DELAY FROM `result_road_saturation_day` a LEFT JOIN info_ ...

  3. 手机uc不支持伪元素使用animation动画;移动端background-attachment:fixed不兼容性

    20170503 1.手机uc不支持伪元素使用animation动画 (暂未解决) 2.移动端background-attachment:fixed不兼容性,没有任何效果, element:befor ...

  4. virtio是啥子

    这个山头今天好像要攻占下来了 guest os中的一些特权操作会被hypervhisor给接收,这里一个很重要的认识是:hypervisor是os的os,既然要访问资源,那么就需要经过整机资源的管理者 ...

  5. Vue父组件与子组件传递事件/调用事件

    1.Vue父组件向子组件传递事件/调用事件 <div id="app"> <hello list="list" ref="child ...

  6. BZOJ 1566 管道取珠(DP)

    求方案数的平方之和.这个看起来很难解决.如果转化为求方案数的有序对的个数.那么就相当于求A和B同时取,最后序列一样的种数. 令dp[i][j][k]表示A在上管道取了i个,下管道取了j个,B在上管道取 ...

  7. 【loj6038】「雅礼集训 2017 Day5」远行 树的直径+并查集+LCT

    题目描述 给你 $n$ 个点,支持 $m$ 次操作,每次为以下两种:连一条边,保证连完后是一棵树/森林:询问一个点能到达的最远的点与该点的距离.强制在线. $n\le 3\times 10^5$ ,$ ...

  8. 在华为eNSP中配置简单的DHCP

    拓扑图,如图1 图1 在AR1中的配置过程如图2 图2 通过PC1查看是否分配了地址,如图3 图3

  9. smb(ms17-010)远程命令执行之msf

    本次用到的环境: kali(2016.2)32位系统.ip地址:192.168.1.104 目标靶机为:win7sp1x64系统(关闭防火墙),ip地址:192.168.1.105 具体的步骤如下: ...

  10. 代码收藏系列--javascript--移动端技巧

    JS判断是否是手机端访问: var is_mobi = navigator.userAgent.toLowerCase().match(/(ipod|iphone|android|coolpad|mm ...