raymarching 算法

raymarching 算法思想很直观:首先有一个3D的体纹理,然后从相机发射n条射线,射线有一个采样的步长。当射线处在体纹理中时,每个步长采一次样,获取纹理值(实际上表示该点的密度值),计算光照,然后和该条射线当前累积的颜色值进行混合。

为什么这样就可以渲染出正确的图案呢?因为光路是可逆的,从光源射出的光线经过散射,最终进入摄像机的效果等同于从摄像机发出的射线进行着色和采样,这个raytracing的道理是一样的。

这种算法很适合在GPU上实现,因为每条射线的计算都是独立并行的,GPU在大量并行计算上有先天的优势。为了在GPU上实现,我们需要解决的问题主要有2个:

  1. 哪些片段需要raymarching。
  2. raymarching的方向和终点在哪里。

下面来逐一进行解决。

确定raymarching的片段

体绘制首先需要一个载体(proxy geometry),也就是为了确定屏幕上的哪些像素是属于某个体纹理的。这个问题很容易就让人联想到包围盒,问题也就引刃而解。

我们只需将体纹理的包围盒绘制出来,那么包围盒在屏幕上覆盖的片段自然就是需要进行raymarching的了。如下图所示:

随后只需要执行raymarching的片断着色器即可。

raymarching的方向和终止点

在使用包围盒作为体绘制的载体时,起/终点就是每根ray进出包围盒时的两个交点。关于如何得到这两个点的坐标,有一种2个pass的算法:

  • 绘制包围盒的背面,即将OpenGL背面剔除设置为GL_FRONT,并将每个片段的世界坐标保存在纹理缓存中。
  • 绘制正面,将每个片段的坐标和上一个pass中的每个片段的坐标相减,即可的到ray的方向和长度,然后进行raymarching算法,达到长度终止即可(采样时要转换为纹理坐标)。

2个pass过程相对繁琐,我使用了1个pass:

  • 获得片段的世界坐标,然后减去视点位置得到ray的方向。然后每次步进时都判断当前的纹理坐标是否超出了包围盒的边界,一旦超出,就停止算法。

确定ray的起始和终止点,是一个对资源消耗很大的过程,这方面也有许多内容值得研究,作为一个探索原理的程序,就以体现算法思想为主了。

顶点着色器如下:

#version 450 corelayout(location = 0) in vec3 vertices;
layout(location = 1) in vec3 UVW;uniform mat4 M;
uniform mat4 V;
uniform mat4 P;
uniform vec3 viewPos;out vec3 textCoord;
out vec3 fragWorldPos;void main()
{gl_Position = P*V*M*vec4(vertices, 1.0);// 把片段的世界坐标以及纹理坐标传递给下一个阶段fragWorldPos = vec3(M*vec4(vertices, 1.0));textCoord = UVW;
}

然后是片断着色器:

#version 450 coreuniform sampler3D noiseSampler;
out vec4 outColor;in vec3 textCoord;
in vec3 fragWorldPos;
uniform vec3 viewPos;uniform mat4 M;struct Ray{vec3 o;vec3 d;
}eyeRay;// 这个着色函数copy的shadertoy上iq大神的,关于体绘制的着色还没有开始深入的学习,日后再说哈哈
vec4 integrate( in vec4 sum, in float dif, in float den, in vec3 bgcol, in float t )
{// lightingvec3 lin = vec3(0.65,0.7,0.75)*1.4 + vec3(1.0, 0.6, 0.3)*dif;        vec4 col = vec4( mix( vec3(1.0,0.95,0.8), vec3(0.25,0.3,0.35), den ), den );col.xyz *= lin;col.xyz = mix( col.xyz, bgcol, 1.0-exp(-0.003*t*t) );// front to back blending    col.a *= 0.2;col.rgb *= col.a;return sum + col*(1.0-sum.a);
}#define CHECK_IN_BOX(p) \
if(p.x < 0.0 || p.x > 1.0\||p.y < 0.0 || p.y > 1.0\||p.z < 0.0 || p.z > 1.0)\break;void main()
{vec3 bgColor = vec3(0.8,0.0,0.4);vec3 lightColor = vec3(0.6, 0.8, 0.7);vec3 lightDir = normalize(vec3(5,5,5));// 射线的起点和方向eyeRay.o = viewPos;// 由于采样时使用的纹理坐标,// 而这个3D纹理坐标系和包围盒的本地坐标系是平行的(原点可能不一致),// 因此将ray的方向向量从世界坐标转换回本地坐标eyeRay.d = inverse(mat3(M)) * normalize(fragWorldPos - viewPos);float stepSize = 0.005;vec4 result = vec4(0.0);// 采样其实是从这个片段的纹理坐标开始的,然后沿着ray的方向步进vec3 p = textCoord;float steps = 0;for(int i = 0; i < 1000; i++, steps++){CHECK_IN_BOX(p);float dens = texture(noiseSampler, p).r;if(dens >= 0.01){float dif = clamp((dens - texture(noiseSampler, p + lightDir *0.5).r), 0.0, 1.0);result = integrate(result, dif, dens, bgColor, stepSize);}p += stepSize*eyeRay.d;}outColor = result;//outColor = vec4(fragWorldPos, 1.0);
}

raymarching算法相关推荐

  1. ShaderToy入门教程(1) - SDF 和 Raymarching 算法

    许多演示场景中使用的技术之一称为 光线追踪(Ray Marching) .该算法与一种称为 有符号距离函数 的特殊函数结合使用,可以实时创建一些非常酷的东西.这是系列教程,陆续推出,这篇涵盖以下黑体所 ...

  2. shader编程-RayMarching与SDF结合开始三维探索(WebGL-Shader开发基础07)

    RayMarching与SDF结合开始三维探索 1. 初识RayMarching 2. 实现过程 2.1 光线步进函数 2.2 获取物体表面距离 2.3 计算光照阴影 2.4 物体表面距离计算 3 d ...

  3. NeurIPS2019获奖论文!7篇论文斩获!微软华裔研究员斩获经典论文

    点上方蓝字计算机视觉联盟获取更多干货 在右上方 ··· 设为星标 ★,与你不见不散 编辑:Sophia 计算机视觉联盟  报道  | 公众号 CVLianMeng 转载于 :https://mediu ...

  4. RayMarching2:给球加上光照

    接上文:RayMarching1:用射线的方式画一个圆 四.法线与光照 如果对偏导或者梯度场有了解,那么对于一个规则的平面想要得到某一点的法线就不是难题 考虑到第一节 SDF 函数 ,我们知道:对于刚 ...

  5. RayMarching1:用射线的方式画一个球

    一.ShaderToy作品 如果你对 Shader 有一定的了解,那么你或多或少听说过 shaderToy 这个网站,这个网站上有很多令人振奋的 shader 效果,而这些效果有可能只用了几行代码来实 ...

  6. ShaderToy入门教程(2) - 光照和相机

    回顾上一篇 ShaderToy入门教程(1) - SDF 和 Raymarching 算法 继续上篇,这篇涵盖以下黑体所示内容 符号距离函数 Ray-marching算法 曲面法线和光照 相机变换 构 ...

  7. PyTorch3D 立体隐式形状渲染:教你构建场景 3D 结构

    内容导读 3D 深度学习一直是机器视觉领域的难点,为了准确高效地建立场景的立体模型,得到相对真实的渲染成果,行业内的一些大厂先后开源了自己的研发成果. 本文首发自微信公众号「PyTorch 开发者社区 ...

  8. 图形学基础|屏幕空间反射(SSR)

    图形学基础|屏幕空间反射(SSR) 文章目录 图形学基础|屏幕空间反射(SSR) 一.前言 二.反射技术概述 2.1 环境贴图反射 2.2 IBL反射 2.3 平面反射(Planar Reflecti ...

  9. nerf-pytorch3D 代码详细流程 debug

    train_nerf.py import main config内容 model class RadianceFieldRenderer def __init__ 分"coarse" ...

最新文章

  1. 内推 58 人拿到微软 offer!这位大佬有多强?
  2. Java I/O Demo
  3. jQuery中的DatePicker今天按钮不起作用
  4. 最简单的c#Remoting编程
  5. 什么是matlab中的fints函数,Matlab基本函数
  6. 二叉树已知先序和中序输出后序
  7. linux macos 界面对比,GNOME 3与Mac OS X 10.7 (Lion)的纵览模式比较
  8. 美柚上云 致力成为最懂女人的互联网企业
  9. 学计算机专业的需要买电脑吗,高三党升级“准大一生”,有必要买电脑吗?学长的回答可以参考...
  10. 生成介于0.95-1的随机数MATLAB,matlab生成随机数函数
  11. mysql 忘记密码处理方式
  12. cad卸载工具_Adobe软件卸载与常见问题解决方案
  13. 基于51单片机的手机电话拨号盘模拟protues仿真
  14. 一些又用的国内著名期刊
  15. iOS开发--微信和支付宝网页支付(过审, 支付宝支付成功可回跳)
  16. c语言删除元素1116,C语言网-1116题-IP判断
  17. Flutter 基础UI功能,常用结构框架代码
  18. http协议及httpd配置
  19. 真无线蓝牙耳机性价比高?真无线蓝牙耳机性价比排行
  20. linux下安装MySQL遇到的坑

热门文章

  1. Appium的Java封装
  2. linux下find命令的使用和总结
  3. 机器学习入门(12)— 激活函数层 ReLU、Sigmoid 层的实现
  4. linux shell下获取cpu温度
  5. 【LeetCode】Palindrome Partitioning 解题报告
  6. 从技术细节看美团的架构
  7. 对面向对象基本原则的总结
  8. 引入yml依赖包_手把手教你发布 Python 项目开源包
  9. 华为手机连电脑_手机装进电脑里 华为MateBook X Pro 2020款升级多屏协同
  10. python数据分析要学什么_python数据分析学什么?python数据分析入门