Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // Raycaster || Began work on 2017/08/19 at 2:36 PM
- // Goal
- // This raycaster aims to have the same functionality and depth of detail as a raytracer, except in two dimensions rather than three. The main feature of raytracing is the nice reflections and shading, along with shadows and light sources. Recreating this in a raycaster is the goal of this project.
- // Capabilities
- // Lighting White light at a fixed unchangeable intensity located at a dynamic point
- // Shadows Illumination cannot travel through objects, resulting in darkness on the opposite side
- // Reflections Rays are reflected until reaching the void or exceeding the maximum depth
- // Shading Colors darken the farther they are from the camera and light sources
- // Squares Squares (cubes, when projected) are the only building block as of now
- // Vectors. Vectors are simply an object containing x number values, where x is the number of dimensions. In the case of a raycaster, which works with two dimensions (although projections are a simulation of three), vectors have two values, x and y. X is the horizontal coordinate, and Y is the vertical coordinate. For x, left is negative and right is positive. For y, down is positive and up is negative.
- function Vect2(x, y) {
- this.x = x;
- this.y = y;
- }
- // Creating ray objects. Ray objects have an origin, that is, where they were casted from. They also have a direction, which is the direction they travel or see in. Both of these values are a two-dimensional vector.
- function Ray(origin, direction) {
- this.origin = origin;
- this.direction = direction;
- }
- // Representing colors. All computer based colors have a red, green and blue channel value. They may also have a simulated alpha channel, which is transparency. This raycaster does not deal with transparency, therefore we omit the alpha channel in our color objects. When drawn to the screen, the alpha channel is 255, or fully opaque.
- function Color(red, green, blue) {
- this.r = red;
- this.g = green;
- this.b = blue;
- }
- // Returning intersections. We will need to find intersections between rays and objects, and after one is found, we will need to return the corresponding data in an object. The data we return will contain the distance travelled before intersecting, and the color of the object that was hit.
- function Intersection(distance, color) {
- this.distance = distance;
- this.color = color;
- }
- // Creating the world
- // The world is represented in an array, consisting of positive numbers (zero inclusive). The array's length must be a square number, as only square worlds are allowed. Any value in the array that is zero will be processed as an empty square, and anything else will be a full square of pallete(v - 1) color.
- var world = [];
- // Generating an empty world with a fixed size. Add (x * x) elements of zero to the array.
- var size = 25;
- for (var n = 0; n < (size * size); n += 1) {
- world.push(0);
- }
- // We should add a few blocks, or it will be boring to look at an empty world. Also, give the block a value of either 1, 2, 3, or 4. This is for the color.
- for (n = 0; n < size; n += 1) {
- world[size * n] = Math.round(3 * Math.random()) + 1;
- }
- // The blocks need somewhere to get the colors from, lets create an array to hold four colors. They will be red, green, blue, and white.
- var palette = [new Color(255, 0, 0), new Color(0, 255, 0), new Color(0, 0, 255), new Color(255, 255, 255)];
- // The background will be colored black
- var background = new Color(0, 0, 0);
- // We now need to create an eye, or a camera, which the world will be visible through. This camera will cast rays along it's field of view (fov) to the world plane. These rays move on to intersect with objects, or the void, and these intersections return the colors that the camera can see. The colors move on to form an image.
- // Remember, the camera's vector must be inside of the worlds boundaries, that is (0 <-> size). It is completely okay for them to be non-whole numbers of course.
- var camera_position = new Vect2(size / 2, size / 2);
- // We need to declare the field of view, or the amount of degrees on each side of the main direction that will be visible. Increasing the fov zooms out, likewise decreasing the fov zooms in.
- var camera_direction = 0; // Pointing straight ahead (going straight right, no upwards movement)
- var field_of_view = 45; // Give ourselves 90 degrees of vision, 45 on either side
- // Also, for extra detail, we add a zoom factor so we can see farther in our direction
- var zoom = 1.5;
- // Make an array to hold all of the scanlines, which are Intersection objects
- var scanlines = [];
- // Now that all of this is ready, we are ready to cast a ray for each line of the screen (we like optimal quality for our renders). Let's loop through every vertical line on the screen.
- for (var x = 0; x < window.innerWidth; x += 1) {
- // Now, let's find the direction that the ray will be sent in for this particular scanline
- var ray_direction = (camera_direction - field_of_view) + (((field_of_view * 2) / window.innerWidth) * x);
- var ray_radians = ray_direction * Math.PI / 180;
- // In the first iteration of this loop, the ray_direction will be (camera_direction - field_of_view). In the final iteration, it will be (camera_direction + field_of_view). Approximately.
- // Now let's find the origin of all the rays, this is simple because it is the camera position!
- var ray_origin = camera_position;
- // Create a ray from all this data
- var ray = new Ray(ray_origin, new Vect2(Math.sin(ray_radians), Math.cos(ray_radians)));
- // Now we need to find the ray's intersections! Let's start by creating an array to hold all the intersections
- var intersections = [];
- // We can now loop through all the blocks (except ones that are empty), and check for intersections!
- for (var b = 0; b < world.length; b += 1) {
- }
- // And add the closest intersection to the scanline array
- scanlines.push(new Intersection(x / 10 / zoom, new Color(255, 255, 255)));
- }
- // Now we need to loop through all the scanlines, and draw them to the screen.
- // Lets get the canvas object, and set it's width and height to fill the screen up
- var canvas = document.getElementById("screen");
- canvas.width = window.innerWidth;
- canvas.height = window.innerHeight;
- // Now get the drawing context, where we draw the scanlines
- var context = canvas.getContext('2d');
- // Clear the canvas
- context.fillStyle = colorString(background);
- context.fillRect(0, 0, canvas.width, canvas.height);
- // Loop through all the scanlines
- for (var s = 0; s < scanlines.length; s += 1) {
- // Get the scanline in question
- var scanline = scanlines[s];
- // Find the length (round it, pixels can't be decimals)
- var length = Math.round(window.innerHeight / scanline.distance);
- // Find the starting point for the scanline
- var start = new Vect2(s, (window.innerHeight / 2) - (length / 2));
- // Find the ending point
- var end = new Vect2(s, (window.innerHeight / 2) + (length / 2));
- // Get the darkened color
- var darken = scanline.distance * 6;
- var color = new Color(scanline.color.r - darken, scanline.color.g - darken, scanline.color.b - darken);
- // Set the stroke color to this color
- context.strokeStyle = colorString(color);
- // Set the width to 2
- context.lineWidth = 2;
- // Start a path, move it to the start, and finish at the end
- context.beginPath();
- context.moveTo(start.x, start.y);
- context.lineTo(end.x, end.y);
- // Stroke the path
- context.stroke();
- }
- // This function is just to create some text that Javascript understands as a color. It takes our Color object, and outputs a string. Not really part of the actual raycaster.
- function colorString(color) {
- return "rgb(" + color.r + "," + color.g + "," + color.b + ")";
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement