当前位置: 首页 > article >正文

GAMES202——作业3 Screen Space Ray Tracing

任务

        为场景实现屏幕空间的全局光照效果

        1.直接光照:  实现ssrFragmentShader中的EvalDiffuse(wi, wo, uv) EvalDirectionalLight(uv) 函数,并在 main 函数中实现直接光照的效果。

        2.屏幕空间光线求交:实现RayMarch(ori, dir, out hitPos) 函数。RayMarch 函数的返回值为是否相交,当相交的时候需要将参数 hitPos设置为交点。参数 ori dir 为世界坐标系中的值,分别代表光线的起点和方向,其中方向向量为单位向量。

        3.间接光照:在 main 函数中实现间接光照,使用蒙特卡洛方法求解渲染方程。

实现

        EvalDiffuse

        该函数是计算diffuse材质的BSDF的,功能简单,其实就是之前101的内容。这里将EvalDiffuse看作渲染方程的f,但是由于计算间接光照的伪代码没有给出cos项,因此这里多乘以一个cos项。另外提一下,这里的INV_PI是PI的倒数,因为在计算机里乘法计算比除法计算更快,因此预定义一个INV_PI能加速渲染。

vec3 EvalDiffuse(vec3 wi, vec3 wo, vec2 uv) {
  vec3 diffuse  = GetGBufferDiffuse(uv);
  vec3 normal = GetGBufferNormalWorld(uv);
  float cos = max(0., dot(normal, wi));
  return diffuse * cos * INV_PI;
}
        EvalDirectionLight

        直接使用题目提供的API,简短两行

vec3 EvalDirectionalLight(vec2 uv) {
  float visibility = GetGBufferuShadow(uv);
  return visibility * uLightRadiance;
}
        RayMarch

        按照202课上的原理,在着色点的位置开始,以某个方向射出光线,一步步向前试探找到交点。有交点的条件是:试探的点的深度比屏幕空间的该位置的深度更深(也就是在屏幕空间一直走直到被遮挡住,这时候说明有交点)。

      

bool RayMarch(vec3 ori, vec3 dir, out vec3 hitPos) {
  //定义一个最大步数防止没有找到交点而无限前进
  const int maxStep = 100;
  float stepSize = 0.05;
  vec3 stepDir = normalize(dir) * stepSize;

  for(int step=0; step< maxStep ; step++){
    float depth = GetDepth(ori);
    vec2 screenUV = GetScreenCoordinate(ori);
    float screenDepth = GetGBufferDepth(screenUV);

    if(depth > screenDepth + 0.0001){
      hitPos = ori;
      return true;
    }

    ori += dir;
  }

  return false;
}
        main里用蒙特卡洛方法解渲染方程

        这里渲染方程没有cos项,cos项在上面的EvalDiffuse已经实现。

        将其转化为代码,这里有需要注意的地方就是direction是局部坐标系的。在101中的作业3里做bumper时,我们使用TBN矩阵来将切线坐标系转化为世界坐标系。将切线坐标系转化为世界坐标系其实还需要uv坐标来确定,因为需要知道某个三角形在纹理上的位置。而这里只涉及方向的转化不涉及位置,所以直接使用TBN矩阵就能获得direction转化到世界坐标的朝向。作业框架里给我们提供了LocalBasis函数来获取两个切线。因此获取坐标系的三个方向后,使用它们创建一个TBN矩阵。

vec3 inDirectLight = vec3(0.0);
  for(int i =0;i<SAMPLE_NUM;i++){
    float pdf;
    vec3 direction = SampleHemisphereUniform(s,pdf);

    vec3 b1,b2;
    LocalBasis(normal,b1,b2);
    mat3 rotateMatrix = mat3(normal,b1,b2);
    direction = normalize(rotateMatrix * direction);
    vec3 hitPos;
    if(RayMarch(pos,direction,hitPos)){
      vec2 hitUV = GetScreenCoordinate(hitPos);
      inDirectLight += EvalDiffuse(direction,wo,uv)/pdf * EvalDiffuse(wi,direction,hitUV) * EvalDirectionalLight(hitUV);
    }
  }
  inDirectLight /= float(SAMPLE_NUM);
  L += inDirectLight;

        整个的main代码

vec3 EvalReflect(vec3 wi,vec3 wo,vec2 uv){
  vec3 pos = GetGBufferPosWorld(uv);
  vec3 normal = GetGBufferNormalWorld(uv);

  vec3 dir = normalize(reflect(-wo,normal) );
  vec3 hitPos;
  if(RayMarch(pos,dir,hitPos)){
    vec2 hitUV = GetScreenCoordinate(hitPos);
    return GetGBufferDiffuse(hitUV);
  }

  return vec3(0.0,0.0,0.0);
}

#define SAMPLE_NUM 3

void main() {
  float s = InitRand(gl_FragCoord.xy);

  vec3 pos = vPosWorld.xyz;
  vec2 uv = GetScreenCoordinate(pos);
  vec3 wi = normalize(uLightDir);
  vec3 wo = normalize(uCameraPos - pos );
  vec3 normal = GetGBufferNormalWorld(uv);


  //vec3 L = vec3(0.0);
  //L = GetGBufferDiffuse(GetScreenCoordinate(vPosWorld.xyz));  //初始的无阴影着色

  vec3 L = EvalDiffuse(wi,wo,uv) * EvalDirectionalLight(uv);  //用于调试带阴影的着色
  //L = ( L + EvalReflect(wi,wo,uv) ) / 2.0;  //用于调试反射

  vec3 inDirectLight = vec3(0.0);
  for(int i =0;i<SAMPLE_NUM;i++){
    float pdf;
    vec3 direction = SampleHemisphereUniform(s,pdf);

    vec3 b1,b2;
    LocalBasis(normal,b1,b2);
    mat3 rotateMatrix = mat3(normal,b1,b2);
    direction = normalize(rotateMatrix * direction);
    vec3 hitPos;
    if(RayMarch(pos,direction,hitPos)){
      vec2 hitUV = GetScreenCoordinate(hitPos);
      inDirectLight += EvalDiffuse(direction,wo,uv)/pdf * EvalDiffuse(wi,direction,hitUV) * EvalDirectionalLight(hitUV);
    }
  }
  inDirectLight /= float(SAMPLE_NUM);
  L += inDirectLight; 

  vec3 color = pow(clamp(L, vec3(0.0), vec3(1.0)), vec3(1.0 / 2.2));
  gl_FragColor = vec4(vec3(color.rgb), 1.0);
}

结果

        无阴影的结果

        含阴影的结果

        调试反射的结果,上面彩色格子的是正方体的面,这里反射的是地板的光。下面灰色的是地板面,反射的是正方体的光照。

        调试间接光照的结果,这里在engine里将cube1换成了cube2。调试cave时,cave是有自己的摄像机属性和光源属性的,需要额外调一下。

        原来硬阴影的cube2

        间接光照的cube2

        硬阴影的cave

实现了间接光照的cave

bonus部分没做了,原理不会很难,就是对RayMarch的优化。但是看其他大佬的作业,大家需要改动的地方特别多,最近没什么时间就没去做了= =


http://www.kler.cn/news/283690.html

相关文章:

  • 创建型设计模式-构建器(builder)模式-python实现
  • 35. 交错动画 导航列表项的悬停和聚焦效果
  • Linux下UDP编程
  • InternVL 多模态模型部署微调实践
  • 物联网平台组件2: 平台校验规则
  • 如何构建社区康养养老系统:Java SpringBoot与Vue实战养老管理系统
  • uniapp的锁屏上文字的显示与隐藏
  • ES6中新增的Set方法详解
  • 小资人群“轻社交”需求与创新营销模式——以“2+1 链动模式小程序、AI 智能名片、S2B2C 商城系统”为例
  • 代码随想录Day 28|题目:122.买卖股票的最佳时机Ⅱ、55.跳跃游戏、45.跳跃游戏Ⅱ、1005.K次取反后最大化的数组和
  • 防抖函数 debounce debouncePromise
  • 获得两类相关点之间的线性关系
  • 简易STL实现 | List的实现
  • 【leetcode刷题记录】二叉树遍历
  • 易查分如何查询图片?
  • 梧桐数据库(WuTongDB):什么是“顺序扫描”
  • 1.3金融术语的宝典
  • PHP房产管理多终端系统灵活应对各种管理需求系统小程序源码
  • 16.神经网络 - 卷积层
  • Python-MNE-源空间和正模型07:修复BEM和头表面
  • Linux 7 静默安装oracle 19c 单机
  • 深度学习常见面试题(2024.8.30笔记)
  • 如何在知行之桥上通过业务单号查找原始报文?
  • 英文论文格式编辑(二)
  • redis list 单推送消息,批量消费消息,springboot实现
  • Nginx配置实例-负载均衡
  • 密码学(二)---DES、SM、RSA
  • c++中的匿名对象及内存管理及模版初阶
  • 【系统架构师软考】计算机网络知识(四)
  • 在类Unix操作系统(如Linux)上运行Windows应用程序方法小记