Javascript-Raytracer着色

问题描述

<!DOCTYPE html>
<html lang="en">
<head>
    <Meta charset="utf-8">
    <title>Raytracer</title>
</head>
<body>
<canvas id='my-canvas'><canvas>

<script src='math/vector3.js'></script>
<script src='math/ray.js'></script>
<script src='math/sphere.js'></script>
<script src='math/plane.js'></script>
<script>

// Canvas setup,don't need to anything in this section ************************
// Get a reference to the javascript object associated with the canvas
var canvas = document.getElementById('my-canvas');

// The canvas dimension we will set
var pixelsAcross = 256;  // pixels across
var pixelsDown = 256; // pixels down

// Set dimensions and pixel scale (generally you can ignore pixelScale,it won't affect your maths)
var pixelScale = 3;
canvas.width = pixelsAcross;
canvas.height = pixelsDown;
canvas.style.csstext = 'width:' + (pixelsAcross * pixelScale) + 'px;height:' + (pixelsDown * pixelScale) + 'px';

// Get the context from the canvas (in this case we just want 2d)
var canvasContext = canvas.getContext('2d');

// Get an array representing all of the pixels
// Arranged left to right,top to bottom
var imageData = canvasContext.getimageData(0,pixelsAcross,pixelsDown);
// end canvas setup section *****************************************************


// Raytracer scene data setup
var fov = 45;   // if this changes,your code should still work
var fovradians = fov * (Math.PI/180);

var pixelWidth = 2 / pixelsAcross;
var pixelHalfWidth = pixelWidth / 2;

// How far away (scalar) the eye is from the image plane (see project guide for more info)
var eyedistance = 1 / Math.tan(fovradians/2);
// -----------
// |         /
// |        /
// |       /
// | 45/2 /      <---- half angle
// |     /
// |    /
// |   /
// |  /
// | /

// important vectors
var eyeCoordinate = new Vector3(0,eyedistance); // all of your rays will use this

// Define the scene objects here ------------------

// These are 3d deFinitions that match all of the math we did in class
// They also provide a "raycast" function that you can pass a ray to
// in order to find intersection between the ray and the object

var sphere = new Sphere(new Vector3(0,0),0.25);
var floor = new Plane(new Vector3(0,1,new Vector3(0,-0.25,0));
var leftWall = new Plane(new Vector3(1,new Vector3(-1,new Vector3(1,0));
var rightWall = new Plane(new Vector3(-1,1));
var ceiling = new Plane(new Vector3(0,-1,0));
var backWall = new Plane(new Vector3(0,1),-1));

var scene = [sphere,ceiling,floor,leftWall,rightWall,backWall];


// end of raytracer setup ******************************************************

// -----------------------------------------------------------------------------
// return a ray that goes from the eye point through the pixel at index (xPixelIndex,yPixelIndex)
function generaterayForPixel(xPixelIndex,yPixelIndex)
{
    var pixelX = -1 + pixelHalfWidth + pixelWidth * xPixelIndex;
    var pixelY = 1 - pixelHalfWidth - pixelWidth * yPixelIndex;
    var pixelCoordinate = new Vector3(pixelX,pixelY,0);

    var direction = new Vector3().test_createFromToVector(eyeCoordinate, pixelCoordinate);

    // Creates a ray from the eye toward the pixelCoordinate
    var pixelRay = new Ray(eyeCoordinate,direction);
    return pixelRay;
}

// -----------------------------------------------------------------------------
function setPixelColor(xPixelIndex,yPixelIndex,color /*[0,1]*/)
{
    var index = (yPixelIndex * pixelsAcross + xPixelIndex) * 4; // 4 bytes per pixel
    imageData.data[index + 0] = color.x * 255; // red channel
    imageData.data[index + 1] = color.y * 255; // green channel
    imageData.data[index + 2] = color.z * 255; // blue channel
    imageData.data[index + 3] = 255;
}

// -----------------------------------------------------------------------------
function updateAndRender(timeElapsed)
{
    var lightPosition = new Vector3(1,0);

    var seconds = timeElapsed * 0.001; // convert milliseconds to seconds
    lightPosition.x = Math.cos(seconds) * 0.5; // radius of 0.5
    lightPosition.z = Math.sin(seconds) * 0.5; // radius of 0.5


    // Go through every pixel
    for (var yPixelIndex = 0; yPixelIndex < pixelsDown; ++yPixelIndex)  //up/down
    {
        for (var xPixelIndex = 0; xPixelIndex < pixelsAcross; ++xPixelIndex)    //left/right
        {
            var pixelRay = generaterayForPixel(xPixelIndex,yPixelIndex);   //current pixel pointed at
        
            // See if the ray intersects something
            var result = {hit: false,point: null,normal: null,distance: null,color: null};  //instatiate result

            for(var i = 0; i < scene.length; i++)   //for each element in scene
            {
                var tempResult = scene[i].raycast(pixelRay);    //see if the pixel hits that scene object and store in temp
                if(tempResult.hit == true)  //if hit that object
                {
                    if(tempResult.distance < result.distance || result.hit == false)    //if closer than whatever was already hit or if hasn't been hit yet
                    {
                        result = tempResult;    //Feed the result of temp
                    }
                }   
            }

            var lightVector = new Vector3().test_createFromToVector(result.point,lightPosition).normalize();   //normallized vector from what was hit to the lightsource

            var shadowRay = new Ray(result.point.clone().add(result.normal.clone().multiplyScalar(.001)),lightVector); //ray from the other side of light to lightvector
            var shadowResult = {hit: false,color: null};    //instatiate existence of shadows

            for(var i = 0; i < scene.length; i++)   //for each element in scene
            {
                var tempResult = scene[i].raycast(shadowRay);   //see if the shadow hits that scene object and store in temp
                if(tempResult.hit == true && (shadowResult.hit == false && shadowResult.distance == null))  //if shadow hits and (no shadow yet and the shadow goes Nowhere)
                {
                    shadowResult = tempResult;  //Feed the result of temp
                }
                if(tempResult.hit == true || (tempResult.hit == false && tempResult.distance != null))  //if shadow hits or (shadow didn't hit and it should go somewhere)
                {
                    if(tempResult.distance < shadowResult.distance || (shadowResult.hit == false && shadowResult.distance == null)) //if the shadow is closer than an existing shadow or (shadow didn't hit and shadow goes Nowhere)
                    {
                        shadowResult = tempResult;  //Feed result of temp
                    }
                }
            }

            var lightdistance = lightPosition.clone().subtract(result.point).length();  //length of lightsource to where was hit on an object

            if((shadowResult.hit == false && shadowResult.distance == null) || (shadowResult.hit == true && shadowResult.distance > lightdistance))
            //if (shadow didn't hit and shadow goes Nowhere) or (shadow hits and the shadow is further than the distance between the light and the object) (i.e. if LoS to light)
            {
                //Color based on Light Source
                var normalDotLight = Math.max(0,result.normal.dot(lightVector));
                var color = new Vector3().copy(result.color);
                color.multiplyScalar(normalDotLight);
                setPixelColor(xPixelIndex,color);
            }
            else
            {
                setPixelColor(xPixelIndex,0); //shadow
            }



/* Old version - B

            // determine which hit object is the closest (in case there is more than 1 hit)
            if (sphereResult.hit == true && planeResult.hit == true)    //if both
            {
                if (sphereResult.distance < planeResult.distance)       //if sphere closer
                {
                    var lightVector = new Vector3().test_createFromToVector(sphereResult.point,lightPosition).normalize();

                    var shadowRay = new Ray(sphereResult.point,lightVector);
                    var shadowResult = sphere.raycast(shadowRay);
                    var lightdistance = lightPosition.clone().subtract(sphereResult.point).length();

                    if(shadowResult.hit == false || (shadowResult.hit == true && shadowResult.distance > lightdistance))    //no shadow or shadow is in front of sphere
                    {
                        var normalDotLight = sphereResult.normal.dot(lightVector);
                        setPixelColor(xPixelIndex,normalDotLight); 
                    }
                    else
                    {
                        setPixelColor(xPixelIndex,0); 
                    }
                }
                else    //plane closer
                {
                    var lightVector = new Vector3().test_createFromToVector(planeResult.point,lightPosition).normalize();

                    var shadowRay = new Ray(planeResult.point,lightVector);
                    var shadowResult = sphere.raycast(shadowRay);
                    var lightdistance = lightPosition.clone().subtract(planeResult.point).length();

                    if(shadowResult.hit == false || (shadowResult.hit == true && shadowResult.distance > distFromLight))
                    {
                        var normalDotLight = planeResult.normal.dot(lightVector);
                        setPixelColor(xPixelIndex,0); 
                    }
                }               
            }
            else if (sphereResult.hit == true || planeResult.hit == true)
            {
                if (sphereResult.hit == true && planeResult.hit == false)       //if only hit sphere
                {
                    var lightVector = new Vector3().test_createFromToVector(sphereResult.point,0); 
                    }
                }
                else    //hit only plane
                {
                    var lightVector = new Vector3().test_createFromToVector(planeResult.point,lightVector);
                    var shadowResult = sphere.raycast(shadowRay);
                    var lightdistance = lightPosition.clone().subtract(planeResult.point).length();

                    if((shadowResult.hit == false && shadowResult.distance == null) || (shadowResult.hit == true && shadowResult.distance > lightdistance)) //prevent crescent
                    {
                        var normalDotLight = planeResult.normal.dot(lightVector);
                        setPixelColor(xPixelIndex,0); 
                    }
                }       
            }
            else    //missed everything
            {
                setPixelColor(xPixelIndex,0.1);
            }
*/
        }
    }

    // Take our array of color data and give to the canvas to display
    canvasContext.putimageData(imageData,0);

    // Let the browser kNow it should call this function again when it's time to render
    requestAnimationFrame(updateAndRender);
}

// We are ready to render,let the browser kNow
requestAnimationFrame(updateAndRender);

</script>
</body>
</html>

Chrome devtools告诉我,“ vector3.js:43 Uncaught TypeError:无法读取未定义的属性'x' 在Vector3.copy(vector3.js:43) 在updateAndRender(raytracer.html:165)上,或者简而言之,result.color永远不会更新。我不确定在这种情况下我到底需要做什么。我是否需要进行另一个场景[i]初始化像我之前那样?

飞机的颜色可以通过Vector3接受给定的rbg值,或者通过(1,1)返回白色,因此应该不会有什么问题,因为它们都是在灰度级下工作的。

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)