You need to enable JavaScript to run this app. 自研光线追踪器实现漫反射时球体透明、噪点问题的调试记录
优惠活动
大模型
产品
解决方案
定价
更多

自研光线追踪器实现漫反射时球体透明、噪点问题的调试记录

自研光线追踪器漫反射渲染异常排查

问题背景

  • 无引导复刻《Ray Tracing in One Weekend》入门渲染逻辑、自研光线追踪器
  • 实现简单漫反射计算环节出现渲染异常:输出的球体呈现透明、带噪点的错误效果
  • 调试阶段核心实现代码见下方代码段

核心实现代码

球体相交检测模块

#pragma once
#include "Camera.h"
#include <cmath>
#include "Defs.h"

template<typename O>
class Sphere{
  O Radius;
  Point3<O> Center;
  Color<O> Col;

public:
  Sphere(O radius, Point3<O> center, Color<O> color);

    O quadratic(Ray<O> ray_in, O &disc, O t_Min, O t_Max);
    bool intersect(Ray<O> ray_in, rayInfo<O> &info, O t_Max);
};

template<typename O>
Sphere<O>::Sphere(O radius, Point3<O> center, Color<O> color) : Radius(radius), Center(center), Col(color) {}

template<typename O>
O Sphere<O>::quadratic(Ray<O> ray_in, O &disc, O t_Min, O t_Max){
  Point3<O> origin = ray_in.Origin;
  Vec3<O> direction = ray_in.Direction;

  Vec3<O> o = origin-Center;
  O a = direction.dot(direction);
  O b = 2 * direction.dot(o);
  O c = o.dot(o) - (Radius * Radius);
  O discriminant = b * b - 4 * (a * c);

  if (discriminant < 0){
      return false;
  }

  disc = ((-b - sqrt(discriminant)) / (2 * a));

  if (disc > t_Max || t_Min > disc){
      disc = ((-b + sqrt(discriminant)) / (2 * a));
      if (disc > t_Max || t_Min > disc){
          return false;
      }
  }

  return true;
}

template<typename O>
bool Sphere<O>::intersect(Ray<O> ray_in, rayInfo<O> &info, O t_Max){

    O disc;

    if (quadratic(ray_in, disc, info.Min, t_Max)){

        Point3<O> p = ray_in.at(disc);
        Vec3<O> normal = (p - Center) / Radius;

        info.Point = p;
        info.Normal = normal;
        info.front_face();
        info.Min = disc;

        return true;
    }
    else{
        return false;
    }
}

路径追踪迭代模块

#pragma once

#include <iostream>
#include "Shapes.h"
#include "Defs.h"
#include "Image.h"

template<typename O>
class Tracer{
  std::vector<Sphere<O>> Shapes;

public:
  Tracer(std::vector<Sphere<O>> shapes);
  void iterator(Ray<O> &ray, O &depth, O t_Max, O t_Min);

};

template<typename O>
Tracer<O>::Tracer(std::vector<Sphere<O>> shapes) : Shapes(shapes) {}

template<typename O>
void Tracer<O>::iterator(Ray<O> &ray, O &depth, O t_Max, O t_Min){

  O conc = 1;
  Color<O> col(0.4f, 0.8f, 0.9f);
  bool hit = false;

  rayInfo<O> info;
  info.Min = t_Min;

  if (depth <= 0)
      conc = 0;

  while (depth > 0){

    for (auto i = Shapes.begin(); i != Shapes.end(); i++){

      if (i->intersect(ray, info, t_Max)){
        conc *= 0.28;
        hit = true;
      }
    }

    if (!hit){
      break;
    }

    Vec3<O> circ = Vec3<O>::random_in_unit_sphere();
    Point3<O> target = info.Point + info.Normal + circ;
    ray = Ray<O>(info.Point, target - info.Point);
    info.Min = t_Min;
    hit = false;
    depth--;
  }

  col = col * conc;
  Image<O>::ColorPixel(std::cout, col);
}

主程序入口模块

#include <iostream>
#include <cmath>
#include "../Matrix.h"
#include "Camera.h"
#include <vector>
#include "Image.h"
#include "Shapes.h"
#include "Tracer.h"
#include "../Defs.h"

template<typename O>
using Point3 = Vec3<O>;

template<typename O>
using Color = Vec3<O>;

int main(){
  const int img_width = 640;
  const int img_height = 480;
  const float img_ratio = img_width/img_height;
  float t_Max = infinity; float t_Min = 0.01; float depth = 50.0f;

  float inv_width = 1 / float(img_width);
  float inv_height = 1 / float(img_height);

  std::vector<Sphere<float>> shapes;

  Camera<float> cam1(40.0f, img_ratio, Point3<float>(0.0f, 0.0f, 0.0f), Point3<float>(0.0f, 0.0f, -1.0f), Vec3<float>(0.0f, 1.0f, 0.0f));

  Sphere<float> cir1(0.4f, Point3<float>(0.0f, 0.0f, -1.0f), Color<float>(0.7f, 0.3f, 0.2f));
  Sphere<float> cir2(3.0f, Point3<float>(0.0f, -3.0f, -1.0f), Color<float>(0.2f, 0.7f, 0.8f));
  Sphere<float> cir3(0.5f, Point3<float>(1.0f, 0.0f, -1.0f), Color<float>(0.2f, 0.3f, 0.7f));
  shapes.push_back(cir1);
  shapes.push_back(cir2);
  shapes.push_back(cir3);

  Tracer<float> tracer(shapes);

  std::cout << "P3\n" << img_width << ' ' << img_height << "\n255" << std::endl;

  Ray<float> ray(Point3<float>(0.0f), Vec3<float>(0.0f));

  for (int j = 0; j < img_height; j++)
  {
    std::cerr << "\rScanlines remaining: " << j << ' ' << std::flush;
    for (int i = 0; i < img_width; i++){
        depth = 50.0f;
        float x = i;
        float y = j;

        cam1.screenCords(x, y, img_width, img_height);

        ray = cam1.get_raydir(x, y);
        tracer.iterator(ray, depth, t_Max, t_Min);
    }
  }
  std::cerr << "\n done " << std::endl;
}

调试过程记录

  • 首次尝试将光强衰减项conc *= 0.28移出形状遍历循环,画面仅能隐约看到阴影轮廓,远未达到预期效果。
  • 根据提示修正逻辑缺陷:原代码在每次相交命中时错误将info.Min设为当前命中距离,正确逻辑应缩小检测的最大距离阈值,避免光线检测到比当前最近命中点更远的物体。
  • 修正上述逻辑缺陷后,添加抗锯齿效果与三次gamma校正,画面质量明显改善,但仍存在渲染异常。
  • 最终定位到根本原因:random_in_unit_sphere()函数的实现错误,采用拒绝采样逻辑修正球内随机点生成方法后,渲染功能恢复正常。

内容的提问来源于stack exchange,提问作者Tippitytopkek

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.11.05 05:45:03