问题描述
<!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 (将#修改为@)