diff --git a/Demo.gif b/Demo.gif
new file mode 100644
index 0000000..e0ffa20
Binary files /dev/null and b/Demo.gif differ
diff --git a/README.md b/README.md
index c636328..062dcfc 100644
--- a/README.md
+++ b/README.md
@@ -5,6 +5,15 @@
(source: Ken Perlin)
+## Result
+
+
+
+I used FBM noise which is adopted from Adam's implementation on Shadertoy and modified it with Iq's warping technique to create such deformed pattern.
+As for the vertex shader, I simply make all vertices bounce from it's original position to a new position using sin function.
+
+Live Link: https://qennys.github.io/hw00-webgl-intro/
+
## Objective
- Check that the tools and build configuration we will be using for the class works.
- Start learning Typescript and WebGL2
diff --git a/src/geometry/Cube.ts b/src/geometry/Cube.ts
new file mode 100644
index 0000000..f69fa6f
--- /dev/null
+++ b/src/geometry/Cube.ts
@@ -0,0 +1,102 @@
+import {vec3, vec4} from 'gl-matrix';
+import Drawable from '../rendering/gl/Drawable';
+import {gl} from '../globals';
+
+
+class Cube extends Drawable {
+ 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.normals = new Float32Array([
+ 0, 0, 1, 0,
+ 0, 0, 1, 0,
+ 0, 0, 1, 0,
+ 0, 0, 1, 0,
+
+ 0, 0, -1, 0,
+ 0, 0, -1, 0,
+ 0, 0, -1, 0,
+ 0, 0, -1, 0,
+
+ -1, 0, 0, 0,
+ -1, 0, 0, 0,
+ -1, 0, 0, 0,
+ -1, 0, 0, 0,
+
+ 1, 0, 0, 0,
+ 1, 0, 0, 0,
+ 1, 0, 0, 0,
+ 1, 0, 0, 0,
+
+ 0, 1, 0, 0,
+ 0, 1, 0, 0,
+ 0, 1, 0, 0,
+ 0, 1, 0, 0,
+
+ 0, -1, 0, 0,
+ 0, -1, 0, 0,
+ 0, -1, 0, 0,
+ 0, -1, 0, 0]);
+
+ this.positions = new Float32Array([
+ -2, -2, 0, 1,
+ 2, -2, 0, 1,
+ 2, 2, 0, 1,
+ -2, 2, 0, 1,
+ // back
+ 2, -2, -4, 1,
+ 2, 2, -4, 1,
+ -2, 2, -4, 1,
+ -2, -2, -4, 1,
+ // left
+ -2, -2, 0, 1,
+ -2, -2, -4, 1,
+ -2, 2, -4, 1,
+ -2, 2, 0, 1,
+ // right
+ 2, -2, 0, 1,
+ 2, -2, -4, 1,
+ 2, 2, -4, 1,
+ 2, 2, 0, 1,
+ // top
+ -2, 2, 0, 1,
+ 2, 2, 0, 1,
+ 2, 2, -4, 1,
+ -2, 2, -4, 1,
+ //bottom
+ -2, -2, -4, 1,
+ 2, -2, -4, 1,
+ 2, -2, 0, 1,
+ -2, -2, 0, 1
+ ]);
+
+ this.generateIdx();
+ this.generateNor();
+ this.generatePos();
+
+ 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('create cube');
+ }
+ };
+
+ export default Cube;
\ No newline at end of file
diff --git a/src/main.ts b/src/main.ts
index 65a9461..c96526f 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -1,4 +1,4 @@
-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';
@@ -7,23 +7,29 @@ import OpenGLRenderer from './rendering/gl/OpenGLRenderer';
import Camera from './Camera';
import {setGL} from './globals';
import ShaderProgram, {Shader} from './rendering/gl/ShaderProgram';
+import Cube from './geometry/Cube';
// Define an object with application parameters and button callbacks
// This will be referred to by dat.GUI's functions that add GUI elements.
const controls = {
tesselations: 5,
'Load Scene': loadScene, // A function pointer, essentially
+ Color: [255.0, 255.0, 255.0]
};
let icosphere: Icosphere;
let square: Square;
+let cube: Cube;
let prevTesselations: number = 5;
+let currentTime: number = 0.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() {
@@ -39,6 +45,7 @@ function main() {
const gui = new DAT.GUI();
gui.add(controls, 'tesselations', 0, 8).step(1);
gui.add(controls, 'Load Scene');
+ gui.addColor(controls, 'Color').onChange(setColor);
// get canvas and webgl context
const canvas = document.getElementById('canvas');
@@ -64,22 +71,36 @@ function main() {
new Shader(gl.FRAGMENT_SHADER, require('./shaders/lambert-frag.glsl')),
]);
+ const noise = new ShaderProgram([
+ new Shader(gl.VERTEX_SHADER, require('./shaders/custom-vert.glsl')),
+ new Shader(gl.FRAGMENT_SHADER, require('./shaders/noise-frag.glsl')),
+ ]);
+
+ noise.setGeometryColor(vec4.fromValues(1.0, 1.0, 1.0, 1.0));
+
// This function will be called every frame
function tick() {
camera.update();
stats.begin();
gl.viewport(0, 0, window.innerWidth, window.innerHeight);
renderer.clear();
+
+ noise.setTime(currentTime);
+ currentTime++;
+
if(controls.tesselations != prevTesselations)
{
prevTesselations = controls.tesselations;
icosphere = new Icosphere(vec3.fromValues(0, 0, 0), 1, prevTesselations);
icosphere.create();
}
- renderer.render(camera, lambert, [
+ renderer.render(camera, noise, [
icosphere,
+ // cube
// square,
]);
+
+
stats.end();
// Tell the browser to call `tick` again whenever it renders a new frame
@@ -98,6 +119,11 @@ function main() {
// Start the render loop
tick();
+
+ function setColor() {
+ noise.setGeometryColor(vec4.fromValues(controls.Color[0] / 255.0, controls.Color[1] / 255.0, controls.Color[2] / 255.0, 1.0));
+ }
}
+
main();
diff --git a/src/rendering/gl/OpenGLRenderer.ts b/src/rendering/gl/OpenGLRenderer.ts
index 7e527c2..3949f6a 100644
--- a/src/rendering/gl/OpenGLRenderer.ts
+++ b/src/rendering/gl/OpenGLRenderer.ts
@@ -25,13 +25,13 @@ class OpenGLRenderer {
render(camera: Camera, prog: ShaderProgram, drawables: Array) {
let model = mat4.create();
let viewProj = mat4.create();
- let color = vec4.fromValues(1, 0, 0, 1);
+ //let color = vec4.fromValues(1, 0, 0, 1);
mat4.identity(model);
mat4.multiply(viewProj, camera.projectionMatrix, camera.viewMatrix);
prog.setModelMatrix(model);
prog.setViewProjMatrix(viewProj);
- prog.setGeometryColor(color);
+ //prog.setGeometryColor(color);
for (let drawable of drawables) {
prog.draw(drawable);
diff --git a/src/rendering/gl/ShaderProgram.ts b/src/rendering/gl/ShaderProgram.ts
index 67fef40..4b6d44f 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,13 @@ class ShaderProgram {
}
}
+ setTime(t: number) {
+ this.use();
+ if (this.unifTime !== -1) {
+ gl.uniform1f(this.unifTime, t);
+ }
+ }
+
draw(d: Drawable) {
this.use();
diff --git a/src/shaders/custom-vert.glsl b/src/shaders/custom-vert.glsl
new file mode 100644
index 0000000..88827e5
--- /dev/null
+++ b/src/shaders/custom-vert.glsl
@@ -0,0 +1,60 @@
+#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
+
+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.
+
+uniform float u_Time;
+
+void main()
+{
+ fs_Col = vs_Col; // Pass the vertex colors to the fragment shader for interpolation
+
+ mat3 invTranspose = mat3(u_ModelInvTr);
+ fs_Nor = vec4(invTranspose * vec3(vs_Nor), 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 below
+ fs_Pos = modelposition;
+
+ vec4 newPos = u_Model * vec4(vs_Pos.xyz * 2.0 , 1);
+ vec4 interpPos = mix(modelposition, newPos, sin(u_Time * 0.01));
+
+ fs_LightVec = lightPos - modelposition; // Compute the direction in which the light source lies
+
+ gl_Position = u_ViewProj * interpPos;// gl_Position is a built-in variable of OpenGL which is
+ // used to render the final positions of the geometry's vertices
+}
diff --git a/src/shaders/lambert-frag.glsl b/src/shaders/lambert-frag.glsl
index 2b8e11b..8a94c87 100644
--- a/src/shaders/lambert-frag.glsl
+++ b/src/shaders/lambert-frag.glsl
@@ -41,3 +41,5 @@ void main()
// Compute final shaded color
out_Col = vec4(diffuseColor.rgb * lightIntensity, diffuseColor.a);
}
+
+
diff --git a/src/shaders/noise-frag.glsl b/src/shaders/noise-frag.glsl
new file mode 100644
index 0000000..2f51a03
--- /dev/null
+++ b/src/shaders/noise-frag.glsl
@@ -0,0 +1,92 @@
+#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 float 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.
+
+#define NUM_OCTAVES 5
+
+float random1(vec2 p) {
+ return fract(sin(dot(p, vec2(456.789, 20487145.123))) * 842478.5453);
+}
+
+float random1( vec3 p ) {
+ return fract(sin(dot(p, vec3(127.1, 311.7, 191.999))) * 43758.5453);
+}
+
+float mySmootherStep(float a, float b, float t) {
+ t = t*t*t*(t*(t*6.0 - 15.0) + 10.0);
+ return mix(a, b, t);
+}
+
+float interpNoise3D1(vec3 p) {
+ vec3 pFract = fract(p);
+ float llb = random1(floor(p));
+ float lrb = random1(floor(p) + vec3(1.0,0.0,0.0));
+ float ulb = random1(floor(p) + vec3(0.0,1.0,0.0));
+ float urb = random1(floor(p) + vec3(1.0,1.0,0.0));
+
+ float llf = random1(floor(p) + vec3(0.0,0.0,1.0));
+ float lrf = random1(floor(p) + vec3(1.0,0.0,1.0));
+ float ulf = random1(floor(p) + vec3(0.0,1.0,1.0));
+ float urf = random1(floor(p) + vec3(1.0,1.0,1.0));
+
+ float lerpXLB = mySmootherStep(llb, lrb, pFract.x);
+ float lerpXHB = mySmootherStep(ulb, urb, pFract.x);
+ float lerpXLF = mySmootherStep(llf, lrf, pFract.x);
+ float lerpXHF = mySmootherStep(ulf, urf, pFract.x);
+
+ float lerpYB = mySmootherStep(lerpXLB, lerpXHB, pFract.y);
+ float lerpYF = mySmootherStep(lerpXLF, lerpXHF, pFract.y);
+
+ return mySmootherStep(lerpYB, lerpYF, pFract.z);
+}
+
+float fbm(vec3 p) {
+ float amp = 0.5;
+ float freq = 18.0;
+ float sum = 0.0;
+ float maxSum = 0.0;
+ for(int i = 0; i < NUM_OCTAVES; ++i) {
+ maxSum += amp;
+ sum += interpNoise3D1(p * freq) * amp;
+ amp *= 0.5;
+ freq *= 2.0;
+ }
+ return sum / maxSum;
+}
+
+float pattern( vec3 p )
+{
+ vec3 q = vec3(fbm(p));
+ q += 0.03*sin( vec3(0.27, 0.23, 0.11) * u_Time * 0.1);
+
+ return fbm( p + 2.0*q );
+}
+
+void main()
+{
+ vec3 color = vec3(1, 0.5, 0.3);
+ color = mix(color, u_Color.rgb, pattern(fs_Nor.xyz));
+ out_Col = vec4(color.rgb, 1);
+}