// @ts-ignore import mat4 from 'gl-mat4'; // @ts-ignore import convert from './objparser'; import $ from 'jquery'; import {initBuffers, deleteBuffers} from './buffers'; import {initShaderProgram} from './shaders'; import {changeFragmentShader} from './changeshader'; let squareRotation = 0.0; main(); /** * The program purpose is encapsulated in a main function */ async function main() { const canvas: any = document.querySelector('#glCanvas')!; const gl = canvas.getContext('webgl'); if (gl == null) { canvas.parentNode.removeChild(canvas); document.getElementById('root')!.insertAdjacentHTML('beforeend', `

Unable to initialize WebGL. Your browser or machine may not support it.

`); } /* eslint-disable */ const vsSource = ` attribute vec4 aVertexPosition; attribute vec4 aVertexColor; attribute vec4 aVertexNormal; uniform mat4 uProjectionMatrix; uniform mat4 uviewMatrix; uniform mat4 umodelMatrix; varying highp vec4 vColor; varying highp vec4 vNormal; varying highp vec3 vPosition; void main() { gl_Position = uProjectionMatrix * uviewMatrix * umodelMatrix * aVertexPosition; vPosition = vec3(aVertexPosition); vColor = aVertexColor; vNormal = umodelMatrix * aVertexNormal; } `; const fsSource = ` precision highp float; varying highp vec4 vColor; varying highp vec4 vNormal; varying highp vec3 vPosition; vec3 extremize(vec3 v, float n) { if (v.x > n / 2.) v.x = n; else v.x = 0.; if (v.y > n / 2.) v.y = n; else v.y = 0.; if (v.z > n / 2.) v.z = n; else v.z = 0.; return v; } void main() { vec3 n = normalize(vec3(-500., 1000., 500.) - vPosition); float diffuse = max(dot(vNormal.xyz, n), 0.); float specular = pow( max(dot( reflect(n, vNormal.xyz), normalize(vec3(0., 0., -50.) - vPosition)), 0.), 10.); vec3 tmp = extremize(mod(vPosition.xyz + vec3(1000.), vec3(2.)), 2.); float texture = (tmp.x + tmp.y + tmp.z) / 6.; if (abs(vNormal.x) + abs(vNormal.y) + abs(vNormal.z) > 0.01) { gl_FragColor = vec4((texture * diffuse * 0.8) + (texture * vec3(0.2)) + (specular * vec3(1.)), 1.0); } else { gl_FragColor = vec4((texture * vec3(1.)), 1.0); } }`; const fsSource2 = ` precision highp float; varying highp vec4 vColor; varying highp vec4 vNormal; varying highp vec3 vPosition; vec3 extremize(vec3 v, float n) { if (v.x > n / 2.) v.x = n; else v.x = 0.; if (v.y > n / 2.) v.y = n; else v.y = 0.; if (v.z > n / 2.) v.z = n; else v.z = 0.; return v; } void main() { vec3 n = normalize(vec3(-500., 1000., 500.) - vPosition); float diffuse = max(dot(vNormal.xyz, n), 0.); float specular = pow( max(dot( reflect(n, vNormal.xyz), normalize(vec3(0., 0., -50.) - vPosition)), 0.), 10.); vec3 texture = extremize(mod(vPosition.xyz + vec3(1000.), vec3(2.)), 2.) / vec3(2); if (abs(vNormal.x) + abs(vNormal.y) + abs(vNormal.z) > 0.01) { gl_FragColor = vec4((texture * diffuse * 0.8) + (texture * vec3(0.2)) + (specular * vec3(1.)), 1.0); } else { gl_FragColor = vec4((texture * vec3(1.)), 1.0); } }`; /* eslint-enable */ /** * Fetch an obj file * @param {string} url the url to fetch the object from * @return {string} the raw data of the obj file */ async function getObj(url: string) { const response = await fetch(url); const data = await response.text(); return data; } const data = await getObj('/static/objs/teapot_normals.obj'); let distance: any = $('#input1').val(); distance = parseFloat(distance); const [ positions, normals, uvs, indices, ] = convert(data); let length = indices.length; console.log(uvs); let [shaderProgram, fragmentShader]: any = initShaderProgram(gl, vsSource, fsSource); let programInfo: any = { program: shaderProgram, attribLocations: { vertexPosition: gl.getAttribLocation(shaderProgram, 'aVertexPosition'), vertexColor: gl.getAttribLocation(shaderProgram, 'aVertexColor'), vertexNormal: gl.getAttribLocation(shaderProgram, 'aVertexNormal'), }, uniformLocations: { projectionMatrix: gl.getUniformLocation( shaderProgram, 'uProjectionMatrix'), viewMatrix: gl.getUniformLocation( shaderProgram, 'uviewMatrix'), modelMatrix: gl.getUniformLocation( shaderProgram, 'umodelMatrix'), }, }; let buffers = initBuffers(gl, positions, indices, normals); let then = 0; let changed = false; /** * Draws the scene repeatedly * @param {number} now the current time */ function render(now: any) { now *= 0.001; const deltaTime = now - then; if (now >= 1 && changed == false) { changed = true; } then = now; drawScene(gl, programInfo, buffers, deltaTime, length, distance); requestAnimationFrame(render); } $(function() { $('#input1').on('keypress', function(event: any) { if (event.which === 13) { event.preventDefault(); $('#button3').click(); } }); $('#button1').on('click', function() { [programInfo, fragmentShader] = changeFragmentShader(gl, shaderProgram, fragmentShader, fsSource, vsSource); }); $('#button2').on('click', function() { [programInfo, fragmentShader] = changeFragmentShader(gl, shaderProgram, fragmentShader, fsSource2, vsSource); }); $('#button3').on('click', function() { distance = $('#input1').val(); distance = parseFloat(distance); }); $('#sphere').on('click', async function() { const data = await getObj('/static/objs/sphere.obj'); const [ positions, normals, uvs, indices, ] = convert(data); console.log(uvs); length = indices.length; deleteBuffers(gl, buffers); buffers = initBuffers(gl, positions, indices, normals); }); $('#teapot_normals').on('click', async function() { const data = await getObj('/static/objs/teapot_normals.obj'); const [ positions, normals, uvs, indices, ] = convert(data); console.log(uvs); length = indices.length; deleteBuffers(gl, buffers); buffers = initBuffers(gl, positions, indices, normals); }); $('#teapot').on('click', async function() { const data = await getObj('/static/objs/teapot.obj'); const [ positions, normals, uvs, indices, ] = convert(data); console.log(uvs); length = indices.length; deleteBuffers(gl, buffers); buffers = initBuffers(gl, positions, indices, normals); }); }); requestAnimationFrame(render); } /** * Draw a webgl scene * @param {any} gl the WebGL context * @param {any} programInfo WebGL program information * @param {any} buffers the buffers to draw * @param {number} deltaTime the difference in time since last call * @param {number} length the index buffer length * @param {number} distance distance of camera */ function drawScene(gl: any, programInfo: any, buffers: any, deltaTime: number, length: number, distance: number) { gl.clearColor(0.0, 0.0, 0.0, 1.0); gl.clearDepth(1.0); gl.enable(gl.DEPTH_TEST); gl.depthFunc(gl.LEQUAL); // Clear the canvas before we start drawing on it. gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); // Create a perspective matrix, a special matrix that is // used to simulate the distortion of perspective in a camera. // Our field of view is 45 degrees, with a width/height // ratio that matches the display size of the canvas // and we only want to see objects between 0.1 units // and 100 units away from the camera. const fieldOfView = 45 * Math.PI / 180; const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight; const zNear = 0.1; const zFar = 150.0; const projectionMatrix = mat4.create(); // note: glmatrix.js always has the first argument // as the destination to receive the result. mat4.perspective( projectionMatrix, fieldOfView, aspect, zNear, zFar); const modelMatrix = mat4.create(); mat4.rotate(modelMatrix, modelMatrix, squareRotation, [ 0, 1, 0, ]); // Set the drawing position to the "identity" point, which is // the center of the scene. const viewMatrix = mat4.create(); mat4.translate( viewMatrix, viewMatrix, [Math.cos(squareRotation) * 5, Math.sin(squareRotation) * 5, 0]); mat4.translate( viewMatrix, viewMatrix, [0.0, 0.0, -distance]); // Tell WebGL how to pull out the positions from the position // buffer into the vertexPosition attribute. { const numComponents = 3; const type = gl.FLOAT; const normalize = false; const stride = 0; const offset = 0; gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position); gl.vertexAttribPointer( programInfo.attribLocations.vertexPosition, numComponents, type, normalize, stride, offset); gl.enableVertexAttribArray( programInfo.attribLocations.vertexPosition); } // Tell WebGL how to pull out the positions from the position // buffer into the vertexPosition attribute. { const numComponents = 3; const type = gl.FLOAT; const normalize = false; const stride = 0; const offset = 0; gl.bindBuffer(gl.ARRAY_BUFFER, buffers.normals); gl.vertexAttribPointer( programInfo.attribLocations.vertexNormal, numComponents, type, normalize, stride, offset); gl.enableVertexAttribArray( programInfo.attribLocations.vertexNormal); } // Tell WebGL how to pull out the positions from the position // buffer into the vertexPosition attribute. { const numComponents = 4; const type = gl.FLOAT; const normalize = false; const stride = 0; const offset = 0; gl.bindBuffer(gl.ARRAY_BUFFER, buffers.color); gl.vertexAttribPointer( programInfo.attribLocations.vertexColor, numComponents, type, normalize, stride, offset); gl.enableVertexAttribArray( programInfo.attribLocations.vertexColor); } // Tell WebGL which indices to use to index the vertices gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffers.indices); // Tell WebGL to use our program when drawing gl.useProgram(programInfo.program); // Set the shader uniforms gl.uniformMatrix4fv( programInfo.uniformLocations.projectionMatrix, false, projectionMatrix); gl.uniformMatrix4fv( programInfo.uniformLocations.viewMatrix, false, viewMatrix); gl.uniformMatrix4fv( programInfo.uniformLocations.modelMatrix, false, modelMatrix); { const vertexCount = length; const type = gl.UNSIGNED_SHORT; const offset = 0; gl.drawElements(gl.TRIANGLES, vertexCount, type, offset); } squareRotation += deltaTime; }