class Custom3DLibrary { constructor(canvasId) { this.canvas = document.getElementById(canvasId); this.gl = this.canvas.getContext("webgl"); if (!this.gl) { alert("WebGL not supported in this browser!"); return; } // Set up WebGL viewport this.gl.viewport(0, 0, this.canvas.width, this.canvas.height); // Create shaders this.vertexShaderSource = ` attribute vec4 a_position; attribute vec3 a_normal; attribute vec3 a_color; uniform mat4 u_modelViewMatrix; uniform mat4 u_projectionMatrix; uniform mat4 u_normalMatrix; varying vec3 v_normal; varying vec3 v_color; void main(void) { gl_Position = u_projectionMatrix * u_modelViewMatrix * a_position; v_normal = mat3(u_normalMatrix) * a_normal; v_color = a_color; // Pass color to fragment shader } `; this.fragmentShaderSource = ` precision mediump float; varying vec3 v_normal; varying vec3 v_color; uniform vec3 u_lightDirection; uniform vec3 u_ambientLight; uniform vec3 u_diffuseLight; uniform float u_transparency; void main(void) { float light = max(dot(normalize(v_normal), normalize(u_lightDirection)), 0.0); vec3 diffuse = light * u_diffuseLight * v_color; gl_FragColor = vec4(diffuse + u_ambientLight, u_transparency); // Apply ambient, diffuse, and transparency } `; // Compile shaders this.vertexShader = this.compileShader(this.vertexShaderSource, this.gl.VERTEX_SHADER); this.fragmentShader = this.compileShader(this.fragmentShaderSource, this.gl.FRAGMENT_SHADER); // Create shader program this.shaderProgram = this.createShaderProgram(this.vertexShader, this.fragmentShader); // Get attribute and uniform locations this.positionLocation = this.gl.getAttribLocation(this.shaderProgram, "a_position"); this.normalLocation = this.gl.getAttribLocation(this.shaderProgram, "a_normal"); this.colorLocation = this.gl.getAttribLocation(this.shaderProgram, "a_color"); this.modelViewMatrixLocation = this.gl.getUniformLocation(this.shaderProgram, "u_modelViewMatrix"); this.projectionMatrixLocation = this.gl.getUniformLocation(this.shaderProgram, "u_projectionMatrix"); this.normalMatrixLocation = this.gl.getUniformLocation(this.shaderProgram, "u_normalMatrix"); this.lightDirectionLocation = this.gl.getUniformLocation(this.shaderProgram, "u_lightDirection"); this.ambientLightLocation = this.gl.getUniformLocation(this.shaderProgram, "u_ambientLight"); this.diffuseLightLocation = this.gl.getUniformLocation(this.shaderProgram, "u_diffuseLight"); this.transparencyLocation = this.gl.getUniformLocation(this.shaderProgram, "u_transparency"); // Scene setup this.objects = []; this.lights = [new Light([1, 1, 1])]; // Default directional light this.cameraPosition = [0, 0, -10]; this.materials = []; // Hold materials for objects this.animationObjects = []; this.time = 0; } // Method to compile shaders compileShader(source, type) { const shader = this.gl.createShader(type); this.gl.shaderSource(shader, source); this.gl.compileShader(shader); if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) { alert("Error compiling shader: " + this.gl.getShaderInfoLog(shader)); } return shader; } // Method to create shader program createShaderProgram(vertexShader, fragmentShader) { const program = this.gl.createProgram(); this.gl.attachShader(program, vertexShader); this.gl.attachShader(program, fragmentShader); this.gl.linkProgram(program); if (!this.gl.getProgramParameter(program, this.gl.LINK_STATUS)) { alert("Error linking program: " + this.gl.getProgramInfoLog(program)); } return program; } // Method to add 3D object to scene addObject(vertices, colors, normals, indices, material) { const object = { vertices: new Float32Array(vertices), colors: new Float32Array(colors), normals: new Float32Array(normals), indices: new Uint16Array(indices), modelMatrix: mat4.create(), material: material || { ambient: [0.2, 0.2, 0.2], diffuse: [1, 1, 1], transparency: 1.0 } }; this.objects.push(object); } // Method to add light to scene addLight(light) { this.lights.push(light); } // Method to set material properties (color, transparency, etc.) setMaterial(material) { this.materials.push(material); } // Set ambient and diffuse lighting setLighting() { for (let i = 0; i < this.lights.length; i++) { this.gl.uniform3fv(this.lightDirectionLocation, this.lights[i].direction); this.gl.uniform3fv(this.ambientLightLocation, this.lights[i].ambient); this.gl.uniform3fv(this.diffuseLightLocation, this.lights[i].diffuse); } } // Method to set the model view matrix (for camera transformations) setModelViewMatrix() { const modelViewMatrix = new Float32Array(16); mat4.identity(modelViewMatrix); mat4.translate(modelViewMatrix, modelViewMatrix, this.cameraPosition); this.gl.uniformMatrix4fv(this.modelViewMatrixLocation, false, modelViewMatrix); } // Method to set the projection matrix (for perspective) setProjectionMatrix() { const projectionMatrix = new Float32Array(16); mat4.perspective(projectionMatrix, Math.PI / 4, this.canvas.width / this.canvas.height, 0.1, 100); this.gl.uniformMatrix4fv(this.projectionMatrixLocation, false, projectionMatrix); } // Method to handle animations animate() { this.time += 0.01; for (const obj of this.animationObjects) { if (obj.animate) { obj.animate(this.time); } } } // Method to render all objects in the scene render() { this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT); this.gl.useProgram(this.shaderProgram); this.gl.enable(this.gl.DEPTH_TEST); this.setProjectionMatrix(); this.setModelViewMatrix(); this.setLighting(); for (let i = 0; i < this.objects.length; i++) { const object = this.objects[i]; const positionBuffer = this.createBuffer(object.vertices, this.gl.ARRAY_BUFFER); const normalBuffer = this.createBuffer(object.normals, this.gl.ARRAY_BUFFER); const colorBuffer = this.createBuffer(object.colors, this.gl.ARRAY_BUFFER); const indexBuffer = this.createBuffer(object.indices, this.gl.ELEMENT_ARRAY_BUFFER); this.gl.uniformMatrix4fv(this.modelViewMatrixLocation, false, object.modelMatrix); this.setNormalMatrix(object.modelMatrix); this.gl.bindBuffer(this.gl.ARRAY_BUFFER, positionBuffer); this.gl.vertexAttribPointer(this.positionLocation, 3, this.gl.FLOAT, false, 0, 0); this.gl.enableVertexAttribArray(this.positionLocation); this.gl.bindBuffer(this.gl.ARRAY_BUFFER, normalBuffer); this.gl.vertexAttribPointer(this.normalLocation, 3, this.gl.FLOAT, false, 0, 0); this.gl.enableVertexAttribArray(this.normalLocation); this.gl.bindBuffer(this.gl.ARRAY_BUFFER, colorBuffer); this.gl.vertexAttribPointer(this.colorLocation, 3, this.gl.FLOAT, false, 0, 0); this.gl.enableVertexAttribArray(this.colorLocation); this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, indexBuffer); // Set transparency for this object this.gl.uniform1f(this.transparencyLocation, object.material.transparency); this.gl.drawElements(this.gl.TRIANGLES, object.indices.length, this.gl.UNSIGNED_SHORT, 0); } this.animate(); } // Method to create buffer createBuffer(data, bufferType) { const buffer = this.gl.createBuffer(); this.gl.bindBuffer(bufferType, buffer); this.gl.bufferData(bufferType, data, this.gl.STATIC_DRAW); return buffer; } } class Light { constructor(direction, ambient = [0.2, 0.2, 0.2], diffuse = [1, 1, 1]) { this.direction = direction; this.ambient = ambient; this.diffuse = diffuse; } } // Start the scene rendering const custom3DScene = new Custom3DLibrary('3d-canvas');