diff --git a/README.md b/README.md index c636328..7812067 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,10 @@ +# Di Lu Results + +![cube](https://user-images.githubusercontent.com/43430369/133516073-d67eeea2-0dfc-4665-917e-58f238e459e7.png) +![cube2](https://user-images.githubusercontent.com/43430369/133516088-40bddfc9-54ae-4ccd-a7c4-2b1d637eb425.png) + +Link: [dluisnothere.github.io/hw00-webgl-intro](https://dluisnothere.github.io/hw00-webgl-intro) + # HW 0: Noisy Planet Part 1 (Intro to Javascript and WebGL)

diff --git a/resultScreenshots/cube.png b/resultScreenshots/cube.png new file mode 100644 index 0000000..6bca6f7 Binary files /dev/null and b/resultScreenshots/cube.png differ diff --git a/resultScreenshots/cube2.png b/resultScreenshots/cube2.png new file mode 100644 index 0000000..80118b5 Binary files /dev/null and b/resultScreenshots/cube2.png differ diff --git a/src/geometry/Cube.ts b/src/geometry/Cube.ts new file mode 100644 index 0000000..19d9c71 --- /dev/null +++ b/src/geometry/Cube.ts @@ -0,0 +1,97 @@ +import {vec3, vec4} from 'gl-matrix'; +import Drawable from '../rendering/gl/Drawable'; +import {gl} from '../globals'; + +class Cube extends Drawable { + buffer: ArrayBuffer; + indices: Uint32Array; + positions: Float32Array; + normals: Float32Array; + center: vec4; + + constructor(center: vec3) { + super(); // Call the constructor of the super class. This is required. + this.center = vec4.fromValues(center[0], center[1], center[2], 1); + } + + + create() { + this.indices = new Uint32Array([0, 1, 2, 0, 2, 3, + 4, 5, 6, 4, 6, 7, + 8, 9, 10, 8, 10, 11, + 12, 13, 14, 12, 14, 15, + 16, 17, 18, 16, 18, 19, + 20, 21, 22, 20, 22, 23]); + + + this.positions = new Float32Array([1, -1, -1, 1, //back + -1, -1, -1, 1, + -1, 1, -1, 1, + 1, 1, -1, 1, + -1, -1, -1, 1, //left + -1, -1, 1, 1, + -1, 1, 1, 1, + -1, 1, -1, 1, + 1, -1, 1, 1, //right + 1, -1, -1, 1, + 1, 1, -1, 1, + 1, 1, 1, 1, + 1, 1, 1, 1, //up + -1, 1, 1, 1, + -1, 1, -1, 1, + 1, 1, -1, 1, + -1, -1, -1, 1, //down + 1, -1, -1, 1, + 1, -1, 1, 1, + -1, -1, 1, 1, + 1, -1, 1, 1, //front + -1, -1, 1, 1, + -1, 1, 1, 1, + 1, 1, 1, 1 + ]); + + this.normals = new Float32Array([0, 0, -1, 0, //back + 0, 0, -1, 0, + 0, 0, -1, 0, + 0, 0, -1, 0, + -1, 0, 0, 0, //left + -1, 0, 0, 0, + -1, 0, 0, 0, + -1, 0, 0, 0, + 1, 0, 0, 0, //right + 1, 0, 0, 0, + 1, 0, 0, 0, + 1, 0, 0, 0, + 0, 1, 0, 0, //up + 0, 1, 0, 0, + 0, 1, 0, 0, + 0, 1, 0, 0, + 0, -1, 0, 0, //down + 0, -1, 0, 0, + 0, -1, 0, 0, + 0, -1, 0, 0, + 0, 0, 1, 0, //front + 0, 0, 1, 0, + 0, 0, 1, 0, + 0, 0, 1, 0, + ]); + + this.generateIdx(); + this.generatePos(); + this.generateNor(); + + this.count = this.indices.length; + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.bufIdx); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.bufNor); + gl.bufferData(gl.ARRAY_BUFFER, this.normals, gl.STATIC_DRAW); + + gl.bindBuffer(gl.ARRAY_BUFFER, this.bufPos); + gl.bufferData(gl.ARRAY_BUFFER, this.positions, gl.STATIC_DRAW); + + console.log(`Created cube`); + } +} + +export default Cube; \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index 65a9461..8ed0149 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,8 +1,9 @@ -import {vec3} from 'gl-matrix'; +import {vec3, vec4} from 'gl-matrix'; const Stats = require('stats-js'); import * as DAT from 'dat.gui'; import Icosphere from './geometry/Icosphere'; import Square from './geometry/Square'; +import Cube from './geometry/Cube'; import OpenGLRenderer from './rendering/gl/OpenGLRenderer'; import Camera from './Camera'; import {setGL} from './globals'; @@ -17,13 +18,18 @@ const controls = { let icosphere: Icosphere; let square: Square; +let cube: Cube; let prevTesselations: number = 5; +let time: number = 0; + function loadScene() { icosphere = new Icosphere(vec3.fromValues(0, 0, 0), 1, controls.tesselations); icosphere.create(); square = new Square(vec3.fromValues(0, 0, 0)); square.create(); + cube = new Cube(vec3.fromValues(0, 0, 0)); + cube.create(); } function main() { @@ -40,6 +46,14 @@ function main() { gui.add(controls, 'tesselations', 0, 8).step(1); gui.add(controls, 'Load Scene'); + // add color controller to gui + var colorPalette = { + colorControls: [0.0, 128.0, 255.0] + } + + var colorController = gui.addColor(colorPalette, 'colorControls'); + + // get canvas and webgl context const canvas = document.getElementById('canvas'); const gl = canvas.getContext('webgl2'); @@ -56,7 +70,7 @@ function main() { const camera = new Camera(vec3.fromValues(0, 0, 5), vec3.fromValues(0, 0, 0)); const renderer = new OpenGLRenderer(canvas); - renderer.setClearColor(0.2, 0.2, 0.2, 1); + renderer.setClearColor(0.17, 0.43, 0.52, 1.0); gl.enable(gl.DEPTH_TEST); const lambert = new ShaderProgram([ @@ -64,8 +78,26 @@ function main() { new Shader(gl.FRAGMENT_SHADER, require('./shaders/lambert-frag.glsl')), ]); + const custom = new ShaderProgram([ + new Shader(gl.VERTEX_SHADER, require('./shaders/custom-vert.glsl')), + new Shader(gl.FRAGMENT_SHADER, require('./shaders/custom-frag.glsl')), + ]); + + var itemColor = vec4.fromValues(colorController.getValue()[0] / 255.0, colorController.getValue()[1] / 255.0, + colorController.getValue()[2] / 255.0, 1.0); + + // This function will be called every frame function tick() { + + //increment time + custom.setTime(time); + time++; + + colorController.onChange(color => { + itemColor = vec4.fromValues(color[0] / 255.0, color[1] / 255.0, color[2] / 255.0, 1.0); + }); + camera.update(); stats.begin(); gl.viewport(0, 0, window.innerWidth, window.innerHeight); @@ -76,10 +108,11 @@ function main() { icosphere = new Icosphere(vec3.fromValues(0, 0, 0), 1, prevTesselations); icosphere.create(); } - renderer.render(camera, lambert, [ + renderer.render(camera, custom /*lambert*/, [ icosphere, - // square, - ]); + //square, + cube + ], itemColor); stats.end(); // Tell the browser to call `tick` again whenever it renders a new frame @@ -95,6 +128,7 @@ function main() { renderer.setSize(window.innerWidth, window.innerHeight); camera.setAspectRatio(window.innerWidth / window.innerHeight); camera.updateProjectionMatrix(); + // Start the render loop tick(); diff --git a/src/rendering/gl/OpenGLRenderer.ts b/src/rendering/gl/OpenGLRenderer.ts index 7e527c2..8e6d34b 100644 --- a/src/rendering/gl/OpenGLRenderer.ts +++ b/src/rendering/gl/OpenGLRenderer.ts @@ -11,7 +11,7 @@ class OpenGLRenderer { setClearColor(r: number, g: number, b: number, a: number) { gl.clearColor(r, g, b, a); - } + } setSize(width: number, height: number) { this.canvas.width = width; @@ -22,10 +22,11 @@ class OpenGLRenderer { gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); } - render(camera: Camera, prog: ShaderProgram, drawables: Array) { + render(camera: Camera, prog: ShaderProgram, drawables: Array, inputColor: vec4) { let model = mat4.create(); let viewProj = mat4.create(); - let color = vec4.fromValues(1, 0, 0, 1); + //let color = vec4.fromValues(1,0,0,1); + let color = inputColor; mat4.identity(model); mat4.multiply(viewProj, camera.projectionMatrix, camera.viewMatrix); diff --git a/src/rendering/gl/ShaderProgram.ts b/src/rendering/gl/ShaderProgram.ts index 67fef40..518f315 100644 --- a/src/rendering/gl/ShaderProgram.ts +++ b/src/rendering/gl/ShaderProgram.ts @@ -29,6 +29,7 @@ class ShaderProgram { unifModelInvTr: WebGLUniformLocation; unifViewProj: WebGLUniformLocation; unifColor: WebGLUniformLocation; + unifTime: WebGLUniformLocation; constructor(shaders: Array) { this.prog = gl.createProgram(); @@ -48,6 +49,7 @@ class ShaderProgram { this.unifModelInvTr = gl.getUniformLocation(this.prog, "u_ModelInvTr"); this.unifViewProj = gl.getUniformLocation(this.prog, "u_ViewProj"); this.unifColor = gl.getUniformLocation(this.prog, "u_Color"); + this.unifTime = gl.getUniformLocation(this.prog, "u_Time"); } use() { @@ -85,6 +87,14 @@ class ShaderProgram { } } + //set time variable + setTime(time: number) { + this.use(); + if (this.unifTime != -1) { + gl.uniform1i(this.unifTime, time); + } + } + draw(d: Drawable) { this.use(); diff --git a/src/shaders/custom-frag.glsl b/src/shaders/custom-frag.glsl new file mode 100644 index 0000000..94ba521 --- /dev/null +++ b/src/shaders/custom-frag.glsl @@ -0,0 +1,115 @@ +#version 300 es + +// This is a fragment shader. If you've opened this file first, please +// open and read lambert.vert.glsl before reading on. +// Unlike the vertex shader, the fragment shader actually does compute +// the shading of geometry. For every pixel in your program's output +// screen, the fragment shader is run for every bit of geometry that +// particular pixel overlaps. By implicitly interpolating the position +// data passed into the fragment shader by the vertex shader, the fragment shader +// can compute what color to apply to its pixel based on things like vertex +// position, light position, and vertex color. +precision highp float; + +uniform vec4 u_Color; // The color with which to render this instance of geometry. +uniform highp int u_Time; + +// These are the interpolated values out of the rasterizer, so you can't know +// their specific values without knowing the vertices that contributed to them +in vec4 fs_Nor; +in vec4 fs_LightVec; +in vec4 fs_Col; +in vec4 fs_Pos; + +out vec4 out_Col; // This is the final output color that you will see on your + // screen for the pixel that is currently being processed. + +float random3D(vec3 p) { + return cos(float(u_Time) * 0.005) * sin(length(vec3( + dot(p, vec3(126.1, 316.8, 106.2)), + dot(p, vec3(266.5, 186.3, 206.4)), + dot(p, vec3(166.4, 246.2, 126.5)) + ) * 0.01 )); +} + +float interpolateNoise3D(float x, float y, float z) +{ + int intX = int(floor(x)); + float fractX = fract(x); + int intY = int(floor(y)); + float fractY = fract(y); + int intZ = int(floor(z)); + float fractZ = fract(z); + + float v1 = random3D(vec3(intX, intY, intZ)); + float v2 = random3D(vec3(intX + 1, intY, intZ)); + float v3 = random3D(vec3(intX, intY + 1, intZ)); + float v4 = random3D(vec3(intX + 1, intY + 1, intZ)); + float v5 = random3D(vec3(intX, intY, intZ + 1)); + float v6 = random3D(vec3(intX + 1, intY, intZ + 1)); + float v7 = random3D(vec3(intX, intY + 1, intZ + 1)); + float v8 = random3D(vec3(intX + 1, intY + 1, intZ + 1)); + + + float i1 = mix(v1, v2, fractX); + float i2 = mix(v3, v4, fractY); + float i3 = mix(v5, v6, fractY); + float i4 = mix(v7, v8, fractZ); + float i5 = mix(v1, v3, fractZ); + float i6 = mix(v2, v4, fractX); + float i7 = mix(v5, v7, fractZ); + float i8 = mix(v6, v8, fractX); + + float mix1 = mix(mix(i1, i2, fractZ), mix(i3, i4, fractX), fractY); + float mix2 = mix(mix(i5, i6, fractX), mix(i7, i8, fractY), fractZ); + float finalMix = mix(mix1, mix2, fractX); + return finalMix; +} + +float fbmNoise(float x, float y, float z) +{ + float total = 0.0; + float persistence = 0.5; + float frequency = 1.0; + float amplitude = 2.0; + int octaves = 5; + + for (int i = 1; i <= octaves; i++) { + total += amplitude * interpolateNoise3D(frequency * x, frequency * y, frequency * z); + frequency *= 3.0; + amplitude *= persistence; + } + return total; +} + +void main() +{ + float noiseValue = fbmNoise(fs_Pos.x, fs_Pos.y, fs_Pos.z); + + vec4 a = vec4(0.5, 0.5, 0.5, 1.0); + vec4 b = vec4(0.5, 0.5, 0.5, 1.0); + vec4 c = vec4(2.0, 1.0, 0.0, 1.0); + vec4 d = vec4(0.5, 0.2, 0.25, 1.0); + + // vec4 a = vec4(0.8, 0.5, 0.4, 1.0); + // vec4 b = vec4(0.2, 0.4, 0.2, 1.0); + // vec4 c = vec4(2.0, 1.0, 1.0, 1.0); + // vec4 d = vec4(0.00, 0.25, 0.25, 1.0); + + vec4 diffuseColor = a + b * cos(6.3 * (c * noiseValue + u_Color + d)); + + // Calculate the diffuse term for Lambert shading + float diffuseTerm = dot(normalize(fs_Nor), normalize(fs_LightVec)); + // Avoid negative lighting values + diffuseTerm = clamp(diffuseTerm, 0.3, 1.0); + + float ambientTerm = 0.2; + + float lightIntensity = diffuseTerm + ambientTerm; //Add a small float value to the color multiplier + //to simulate ambient lighting. This ensures that faces that are not + //lit by our point light are not completely black. + + // Compute final shaded color + out_Col = vec4(diffuseColor.rgb * lightIntensity, diffuseColor.a); +} + diff --git a/src/shaders/custom-vert.glsl b/src/shaders/custom-vert.glsl new file mode 100644 index 0000000..d09e536 --- /dev/null +++ b/src/shaders/custom-vert.glsl @@ -0,0 +1,77 @@ +#version 300 es + +//This is a vertex shader. While it is called a "shader" due to outdated conventions, this file +//is used to apply matrix transformations to the arrays of vertex data passed to it. +//Since this code is run on your GPU, each vertex is transformed simultaneously. +//If it were run on your CPU, each vertex would have to be processed in a FOR loop, one at a time. +//This simultaneous transformation allows your program to run much faster, especially when rendering +//geometry with millions of vertices. + +uniform mat4 u_Model; // The matrix that defines the transformation of the + // object we're rendering. In this assignment, + // this will be the result of traversing your scene graph. + +uniform mat4 u_ModelInvTr; // The inverse transpose of the model matrix. + // This allows us to transform the object's normals properly + // if the object has been non-uniformly scaled. + +uniform mat4 u_ViewProj; // The matrix that defines the camera's transformation. + // We've written a static matrix for you to use for HW2, + // but in HW3 you'll have to generate one yourself + +uniform highp int u_Time; + +in vec4 vs_Pos; // The array of vertex positions passed to the shader + +in vec4 vs_Nor; // The array of vertex normals passed to the shader + +in vec4 vs_Col; // The array of vertex colors passed to the shader. + +out vec4 fs_Nor; // The array of normals that has been transformed by u_ModelInvTr. This is implicitly passed to the fragment shader. +out vec4 fs_LightVec; // The direction in which our virtual light lies, relative to each vertex. This is implicitly passed to the fragment shader. +out vec4 fs_Col; // The color of each vertex. This is implicitly passed to the fragment shader. +out vec4 fs_Pos; + +const vec4 lightPos = vec4(5, 5, 3, 1); //The position of our virtual light, which is used to compute the shading of + //the geometry in the fragment shader. + +float random(float inputValue) { + return fract((sin(inputValue)) * + 43758.5453123); +} + +vec4 perturbVec(vec4 original) +{ + vec4 randomized = vec4(random(original.x), random(original.y) + cos(original.z), + random(original.z) + sin(original.x), 1.0); + return mix(randomized, original, cos(float(u_Time) * 0.005)); +} + +void main() +{ + fs_Col = vs_Col; // Pass the vertex colors to the fragment shader for interpolation + + vec4 temp = vec4(normalize(vec3(vs_Pos.xyz)), 1.0); + + vec4 noisedVec = u_Model * mix(perturbVec(vs_Pos), temp, cos(float(u_Time) * 0.001)); + + mat3 invTranspose = mat3(u_ModelInvTr); + fs_Nor = vec4(invTranspose * vec3(vs_Nor), 0.0); // Pass the vertex normals to the fragment shader for interpolation. + // Transform the geometry's normals by the inverse transpose of the + // model matrix. This is necessary to ensure the normals remain + // perpendicular to the surface after the surface is transformed by + // the model matrix. + + vec4 modelposition = u_Model * vs_Pos; // Temporarily store the transformed vertex positions for use belo + + + //vec4 perturbedVec = perturbVec(modelposition) * vs_Nor; + + fs_LightVec = lightPos - modelposition; // Compute the direction in which the light source lies + + fs_Pos = vs_Pos; + + //gl_Position = u_ViewProj * modelposition + random(vs_Pos.x) * perturbedVec;// gl_Position is a built-in variable of OpenGL which is + // used to render the final positions of the geometry's vertices + gl_Position = u_ViewProj * noisedVec; +}