在Unity中实现屏幕空间反射Screen Space Reflection(2)
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)的更多相关文章
- 在Unity中实现屏幕空间反射Screen Space Reflection(4)
第四部分讲一下如何在2D屏幕空间步进光线. http://casual-effects.blogspot.com/2014/08/screen-space-ray-tracing.html 中的代码感 ...
- 在Unity中实现屏幕空间反射Screen Space Reflection(1)
本篇文章我会介绍一下我自己在Unity中实现的SSR效果 出发点是理解SSR效果的原理,因此最终效果不是非常完美的(代码都是够用就行),但是从学习的角度来说足以学习到SSR中的核心算法. 如果对核心算 ...
- 在Unity中实现屏幕空间反射Screen Space Reflection(3)
本篇讲一下相交检测的优化.有两个措施. 线段相交检测 之前的检测都是检测光线的终点是否在物体内.我们可以尝试检测光线的线段是否与物体相交. 比如说有一个非常薄的物体,光线差不多垂直于它的表面.如果用普 ...
- 高级屏幕空间反射: Screen Space Reflection (SSSR)
SSSR进一步调优,对标寒霜级技术水平,实现方式为Direct3D 11+自主实现实时渲染引擎,方法为对比测试.实现已经有段时间了,还是简要更新下吧.以下画面中的SSSR效果全部采用1:4 resol ...
- 高级屏幕空间反射: Screen Space Reflection (SSR)
自从CE3首倡SSR以来,发展至今,其质量与当年早已不能同日而语.不仅强调超越性的质量,而且强调超越性的性能.乘着周末有空撸了撸,以下是增强型实时SSR结果图.与我原来的SSR原始实现相比,新的增强型 ...
- screen space reflection/soft alpha test/
http://www.crytek.com/cryengine/presentations/secrets-of-cryengine-3-graphics-technology 很多宝贝里面 不止题目 ...
- 在Unity中实现屏幕空间阴影(1)
接着上篇文章,我们实现了SSR效果. 其中的在屏幕空间进行光线追踪的方法是通用的.借此我们再实现一种屏幕空间的效果,即屏幕空间阴影. 文中的图片来自Catlike coding http://catl ...
- 在Unity中实现屏幕空间阴影(2)
参考文章: https://www.imgtec.com/blog/implementing-fast-ray-traced-soft-shadows-in-a-game-engine/ 完成的工程: ...
- 关于Unity中的屏幕适配
一.Game视图的屏幕分辨率可以先自定义添加,供以后选择,以下是手游经常用到的分辨率: 1.1136X640,iPhone5 2.1920X1080,横屏,主流游戏都是这个分辨率 3.1080X192 ...
随机推荐
- MySQL 基于mysqldump备份工具实战演练
前言: 细节提示:先执行 show global variables like 'log_bin';看看log_bin的值,如果服务器变量log_bin的值为OFF,需要修改my.cnf配置文件,将l ...
- HTML图片标签
<body> <!-- 使用img标签来向网页中引入外部的图片, img标签也是一个自结束标签 属性: src:设置一个外部图片的路径 alt:可以用来设置图片不能显示时,就会显示图 ...
- c99标准的restrict关键字
参考自restrict restrict解释 restrict关键字出现于C99标准,wiki上的解释restrict from wiki. In the C programming language ...
- PAT L1-032 Left-pad
https://pintia.cn/problem-sets/994805046380707840/problems/994805100684361728 根据新浪微博上的消息,有一位开发者不满NPM ...
- PHP 操作redis 详细讲解 转的 http://www.cnblogs.com/jackluo/p/3412670.html
phpredis是redis的php的一个扩展,效率是相当高有链表排序功能,对创建内存级的模块业务关系 很有用;以下是redis官方提供的命令使用技巧: 下载地址如下: https://github. ...
- delphi完美经典--第十八章
第18章数据感知组件 一.TDBText组件 用来以只读.一次一条记录的方式,显示DataSet中的某一字段值.因同样继承自TCustomLabel,TDBText组件除了数据感知功能外,与标准组件T ...
- 第129天:node.js安装方法
node.js安装方法 第一步:双击node.js安装包开始安装,注意64位和32位,按照自己的进行安装 第二步:在安装过程中一直选择next,在选择安装目录时,大多数默认安装在C盘,我安装在了D盘, ...
- P1107 [BJWC2008]雷涛的小猫
题目描述 雷涛同学非常的有爱心,在他的宿舍里,养着一只因为受伤被救助的小猫(当然,这样的行为是违反学生宿舍管理条例的).在他的照顾下,小猫很快恢复了健康,并且愈发的活泼可爱了. 可是有一天,雷涛下课回 ...
- NOIP2003 传染病控制 【搜索 + 卡时】
题目背景 近来,一种新的传染病肆虐全球.蓬莱国也发现了零星感染者,为防止该病在蓬莱国大范围流行,该国政府决定不惜一切代价控制传染病的蔓延.不幸的是,由于人们尚未完全认识这种传染病,难以准确判别病毒携带 ...
- 小Z的袜子 题解报告【莫队】
Description 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命-- 具体来说,小Z把这N只 ...