在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 ...
随机推荐
- 浅谈 Sql Server 游标
查询语句可能返回多条记录,如果数据量非常大,需要使用游标来逐条读取查询结果集中的记录.应用程序可以根据需要滚动或浏览其中的数据.本篇介绍游标的概念.分类.以及基本操作等内容. 一:认识游标游标是SQL ...
- C语言语法树
- 上传web端——个人项目
我用visual studio新建了一个web窗口,如图: 然后这里是系统自带的代码: [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile ...
- AdminLTE 框架应用(一 )- 插件介绍
原AdminLTE中的插件让我大部分都移除了,第一是占地方,需要的时候再引入也不迟,第二就是有些插件已经过时了,有比较好的插件可以替代.附上项目插件截图 1.bootstrap-addTabs 提供多 ...
- 如何更好的使用JAVA线程池
这篇文章结合Doug Lea大神在JDK1.5提供的JCU包,分别从线程池大小参数的设置.工作线程的创建.空闲线程的回收.阻塞队列的使用.任务拒绝策略.线程池Hook等方面来了解线程池的使用,其中涉及 ...
- XTU 1233 Coins(DP)
题意: n个硬币摆成一排,问有连续m个正面朝上的硬币的序列种数. 很明显的DP题.定义状态dp[i][1]表示前i个硬币满足条件的序列种数.dp[i][0]表示前i个硬币不满足条件的序列种数. 那么显 ...
- robot framework Selenium2关键字介绍
*** Settings *** Library Selenium2Library *** Keywords *** Checkbox应该不被选择 [Arguments] ${locator} Che ...
- CF878C Tournament set 图论
题面 题面 题解 如果2个人可以互相战胜,那么我们连一条无向边,于是最后会剩下t个联通块,其中每对联通块之间都有严格的大小关系(a.max < b.min),因此我们每插入一个点就相当于合并一段 ...
- 【BZOJ2878】【NOI2012】迷失游乐园(动态规划)
[BZOJ2878][NOI2012]迷失游乐园(动态规划) 题面 BZOJ 题解 记得以前考试的时候做过这道题目 这题的暴力还是非常显然的,每次\(dfs\)一下就好了. 时间复杂度\(O(n^2) ...
- 温习classList api
有道题是一个removeClass的功能,代码里是正则分隔了传入的name,根据name的个数,循环移除掉,让寻找bug..看了了这几行代码,首先想到的是我会如何去优化. 如果看代码一两分钟就能找到公 ...