光线追踪:出现暗环

Raytracing: Dark rings appear

当我使用光照进行渲染时,我的球体上出现了奇怪的黑色环。我刚刚添加了照明,但我不明白为什么会产生黑环。

这是我的示踪剂代码。

public class Tracer {

    public Camera Cam;
    public int Width, Height;
    public BufferedImage Image;
    public Color BackGroundColor;
    public int StartX, StartY, EndX, EndY,RowCount,ColCount;
    public ArrayList<GeometricObject> GeoObjects;
    public ArrayList<LightObject> LightObjects;
    public boolean Tracing;
    public double AmbientLight;

    public Tracer(Camera cam, int width, int height, BufferedImage image, Color backGroundColor, int startX, int startY, int endX, int endY, int rowCount, int colCount, ArrayList<GeometricObject> Geoobjects,ArrayList<LightObject> Lightobjects,double ambientLight) {
        super();
        Cam = cam;
        Width = width;
        Height = height;
        Image = image;
        BackGroundColor = backGroundColor;
        StartX = startX;
        StartY = startY;
        EndX = endX;
        EndY = endY;
        RowCount = rowCount;
        ColCount = colCount;
        GeoObjects = Geoobjects;
        LightObjects = Lightobjects;
        if(ambientLight > 1){
            AmbientLight = 1;
        }else if(ambientLight < 0){
            AmbientLight = 0;
        }else{
            AmbientLight = ambientLight;
        }
    }

    public void TracePixelFast(int x, int y) {
        Color color = new Color(BackGroundColor.r,BackGroundColor.g,BackGroundColor.b);
        for(int o = 0;o < GeoObjects.size();o++){
            GeometricObject GO = GeoObjects.get(o);
            Ray r = new Ray(Cam.GetRayPos(Width, Height, x, y, 1, 1, RowCount, ColCount), Cam.GetRayDir(Width, Height, x, y, 1,1, RowCount, ColCount));
            double hit = GO.hit(r);
            if (hit != 0.0) {
                color = Cal_Pixel(x,y);
                Image.setRGB(x, y, color.toInt());
                break;
            }
        }
    }

    public void TracePixelSmooth(int x, int y) {
        Image.setRGB(x, y,Cal_Pixel(x,y).toInt());
    }

    public Color Cal_Pixel(int x,int y){
        Color color = new Color(BackGroundColor);
        Color colorh = new Color(BackGroundColor);
        Color bgc = new Color(BackGroundColor);
        int HIT = 0;
        int MISS = 0;
        for (int row = 0; row < RowCount; row++) {
            for (int col = 0; col < ColCount; col++) {
                double min = Double.MAX_VALUE;
                for (int o = 0; o < GeoObjects.size(); o++) {
                    GeometricObject GO = GeoObjects.get(o);
                    Ray r = new Ray(Cam.GetRayPos(Width, Height, x, y, row, col, RowCount, ColCount),Cam.GetRayDir(Width, Height, x, y, row, col, RowCount, ColCount));
                    double hit = GO.hit(r);
                    if (hit != 0.0 && hit < min) {
                        min = hit;
                        colorh = ShadePixel(GO, r, hit);
                        HIT++;
                    } else {
                        double min2 = Double.MAX_VALUE;
                        for (int o2 = 0; o2 < GeoObjects.size(); o2++) {
                            if(o!=o2){
                            GeometricObject GO2 = GeoObjects.get(o2);
                            double hit2 = GO2.hit(r);
                            if (hit2 != 0.0 && hit2 < min2) {
                            min2 = hit2;
                            bgc = ShadePixel(GO2, r, hit2);
                            }
                         }
                        }
                        MISS++;
                    }
                }
            }
        }

        for(int h = 0;h < HIT;h++){
            color.Add(colorh);
        }
        for(int m = 0;m < MISS;m++){
            color.Add(bgc);
        }
        color.Divide(RowCount * ColCount);
        return color;
    }

    public Color ShadePixel(GeometricObject GO,Ray ray,double t){
        ArrayList<Color> PixelShade = new ArrayList<Color>();
        Normal normal = GO.Cal_Normal(ray, t);
        for(int l = 0;l < LightObjects.size();l++){
            LightObject light = LightObjects.get(l);
            Vector3D r_Dir = light.Pos.Sub(normal.Origin);
            r_Dir.normalize();
            Ray raytolight = new Ray(normal.Origin,r_Dir);
            int WAS_HIT = 0;
            for(int o = 0;o < GeoObjects.size();o++){
                GeometricObject NGO = GeoObjects.get(o);
                double hit = NGO.hit(raytolight);
                if (hit != 0.0) {
                   WAS_HIT = 1;
                }
            }
            if(WAS_HIT == 0){
                 double Dot = normal.Direction.Dot(r_Dir);
                 if(Dot < 0){
                    Dot = 0;
                 }
                 double Diffuse = 1 - AmbientLight;
                 Color color = new Color(GO.Color);
                 double Shade = AmbientLight + Diffuse*Dot;
                 color.Mul(Shade);
                 PixelShade.add(color);
            }else{
                 Color color = new Color(GO.Color);
                 double Shade = AmbientLight;
                 color.Mul(Shade);
                 PixelShade.add(color);
            }
        }
        Color Final = new Color();
        for(int s = 0;s < PixelShade.size();s++){
            Final.Add(PixelShade.get(s));
        }
        Final.Divide(PixelShade.size());
        return Final;
    }

    public void TraceArea(boolean SmoothTracing) {
        Tracing = true;
        if(SmoothTracing){
            for (int x = StartX; x < EndX; x++) {
                for (int y = StartY; y < EndY; y++) {
                    TracePixelSmooth(x,y);
                }
            }
        }else{
            for (int x = StartX; x < EndX; x++) {
                for (int y = StartY; y < EndY; y++) {
                    TracePixelFast(x,y);
                }
            }
        }
    }
}

这是球体的代码。

public class Sphere extends GeometricObject{

    public Vector3D Center;
    public double Radius;

    public Sphere(Vector3D Center,double Radius,Color Color){
        this.Center = Center;
        this.Radius = Radius;
        this.Color = Color;
    }

    public double hit(Ray ray) {
        double a = ray.Direction.Dot(ray.Direction);
        double b = 2 * ray.Origin.Sub(Center).Dot(ray.Direction);
        double c = ray.Origin.Sub(Center).Dot(ray.Origin.Sub(Center))-Radius*Radius;
        double discreminant = b*b-4*a*c;
        if(discreminant < 0.0f){
            return 0.0;
        }else{
            double t = (-b - Math.sqrt(discreminant))/(2*a);
            if(t > 10E-9){
                return t;
            }else{
                return 0.0;
            }
        }
    }

    public Normal Cal_Normal(Ray ray,double t) {
        Vector3D NPos = new Vector3D(ray.Origin.x + ray.Direction.x*t,ray.Origin.y + ray.Direction.y*t,ray.Origin.z + ray.Direction.z*t);
        Vector3D NDir = NPos.Sub(Center).Div(Radius);
        return new Normal(NPos,NDir);
    }
}

我确定问题出在 shadepixel() 但我可能错了。 我刚刚发现我添加的对象越多,环就越多:
1 个对象没有环。
2 个对象 1 个戒指。
3 个物体 2 个环。

如果你需要我 post 更多我的 code.Just 问我会的。

当我从学校回来后,我会 post 我的颜色 class 并解决颜色问题。我仍然不明白为什么我添加的对象(球体)越多,环就越多。任何人都可以向我解释为什么会这样吗?

这是我的颜色代码。

public class Color {

public float r,g,b;

public Color(){
    r = 0.0f;
    g = 0.0f;
    b = 0.0f;
}

public Color(float fr,float fg,float fb){
    r = fr;
    g = fg;
    b = fb;
}

public Color(Color color){
    r = color.r;
    g = color.g;
    b = color.b;
}

public void Add(Color color){
    r += color.r;
    g += color.g;
    b += color.b;
}

public void Divide(int scalar){
    r /= scalar;
    g /= scalar;
    b /= scalar;
}

public void Mul(double mul){
    r *= mul;
    g *= mul;
    b *= mul;
}

public int toInt(){
    return (int) (r*255)<<16 | (int) (g*255)<<8 | (int) (b*255); 
}

此代码存在多个问题,但环的直接原因是颜色分量值超出了 0-255 范围。反过来,这是由于我认为 Cal_Pixel() 中的抗锯齿尝试的计算不正确以及 ShadePixel().

中没有控制任何数字范围造成的

想一想如何直观地调试这个场景。

首先法线正确吗?将它们显示为要查看的颜色。

将每个分量的范围 [-1..1] 取到范围 [0..255]:

r = 255*(n.x + 1)/2;
g = 255*(n.y + 1)/2;
b = 255*(n.z + 1)/2;

一旦您认为它们看起来正确,就进入下一阶段并逐步建立它。

例如你可能会看看你的点积是否符合预期(再次 [-1..1] 因为向量应该是标准化的):

r = 255*(dot + 1)/2;
g = 255*(dot + 1)/2;
b = 255*(dot + 1)/2;