"use strict"; // 參考 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode //////////////////////////////////////////////////////////////////////////////// // 程式貼圖 // 你的作業是修改 Fragment Shader,只應該修改 fragmentShader 變數 //////////////////////////////////////////////////////////////////////////////// var camera, scene, renderer, light; var cameraControls, effectController, phongMaterial; var clock = new THREE.Clock(); var vertexShader = ` varying vec3 vNormal; varying vec3 vViewPosition; varying vec3 vModelPosition; void main() { gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); vNormal = normalize( normalMatrix * normal ); vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 ); vViewPosition = -mvPosition.xyz; vModelPosition = position; } ` var fragmentShader = ` uniform vec3 uMaterialColor; uniform vec3 uDirLightPos; uniform vec3 uDirLightColor; uniform float uKd; uniform float uScale; varying vec3 vNormal; varying vec3 vViewPosition; varying vec3 vModelPosition; void main() { // 計算往光源的方向 vec4 lDirection = viewMatrix * vec4( uDirLightPos, 0.0 ); vec3 lVector = normalize( lDirection.xyz ); // 漫射:N * L。法向量必須正規化,因為經過內插計算 vec3 normal = normalize( vNormal ); // 學生作業:用 vModelPosition 作為函數輸入,然後將 diffuse 乘上回傳值 float diffuse = max( dot( normal, lVector ), 0.0); gl_FragColor = vec4( uKd * uMaterialColor * uDirLightColor * diffuse, 1.0 ); } ` function fillScene() { scene = new THREE.Scene(); scene.fog = new THREE.Fog( 0xAAAAAA, 2000, 4000 ); // 光源 var ambientLight = new THREE.AmbientLight(0x333333); // 0.2 light = new THREE.DirectionalLight(0xFFFFFF, 1.0); light.position.set(320, 390, 700); scene.add(ambientLight); scene.add(light); var teapotSize = 400; var materialColor = new THREE.Color(); materialColor.setRGB(1.0, 0.8, 0.6); phongMaterial = createShaderMaterial("phongDiffuse", light); phongMaterial.uniforms.uMaterialColor.value.copy(materialColor); phongMaterial.side = THREE.DoubleSide; var teapot = new THREE.Mesh( new THREE.TeapotGeometry(teapotSize, 20, true, true, true, true), phongMaterial); scene.add(teapot); } function init() { canvasWidth = window.innerWidth; canvasHeight = window.innerHeight; var canvasRatio = canvasWidth/canvasHeight; // RENDERER renderer = new THREE.WebGLRenderer( { antialias: true } ); renderer.gammaInput = true; renderer.gammaOutput = true; renderer.setSize(canvasWidth, canvasHeight); renderer.setClearColorHex( 0xAAAAAA, 1.0 ); // 攝影機 camera = new THREE.PerspectiveCamera( 45, canvasRatio, 1, 80000 ); camera.position.set(-600, 900, 1300); // 控制 cameraControls = new THREE.OrbitAndPanControls(camera, renderer.domElement); cameraControls.target.set(0, 0, 0); } function addToDOM() { var container = document.getElementById('container'); var canvas = container.getElementsByTagName('canvas'); if (canvas.length>0) { container.removeChild(canvas[0]); } container.appendChild( renderer.domElement ); } function animate() { window.requestAnimationFrame(animate); render(); } function render() { var delta = clock.getDelta(); cameraControls.update(delta); // 從 UI 取輸入來修改 Shader 的值 phongMaterial.uniforms.uKd.value = effectController.kd; phongMaterial.uniforms.uScale.value = effectController.scale; var materialColor = new THREE.Color(); materialColor.setHSL(effectController.hue, effectController.saturation, effectController.lightness); phongMaterial.uniforms.uMaterialColor.value.copy(materialColor); light.position.set(effectController.lx, effectController.ly, effectController.lz); light.color.setHSL(effectController.lhue, effectController.lsaturation, effectController.llightness); renderer.render(scene, camera); } function createShaderMaterial(id, light) { // 可以是全域變數,只定義一次,但為了方便放在這裡 var shaderTypes = { 'phongDiffuse' : { uniforms: { "uDirLightPos": { type: "v3", value: new THREE.Vector3() }, "uDirLightColor": { type: "c", value: new THREE.Color( 0xFFFFFF ) }, "uMaterialColor": { type: "c", value: new THREE.Color( 0xFFFFFF ) }, uKd: { type: "f", value: 0.7 }, uScale: { type: "f", value: 0.2 } } } }; var shader = shaderTypes[id]; var u = THREE.UniformsUtils.clone(shader.uniforms); var vs = vertexShader; var fs = fragmentShader; var material = new THREE.ShaderMaterial({ uniforms: u, vertexShader: vs, fragmentShader: fs }); material.uniforms.uDirLightPos.value = light.position; material.uniforms.uDirLightColor.value = light.color; return material; } function setupGui() { effectController = { kd: 0.7, scale: 0.2, hue: 0.463, saturation: 0.46, lightness: 0.7, lhue: 0.04, lsaturation: 0.01, llightness: 0.8, // 奇怪的是如果你以負數初始化,這些滑桿不會顯示小數位 lx: 0.32, ly: 0.39, lz: 0.7 }; var h; var gui = new dat.GUI(); // 材質(屬性) h = gui.addFolder("材質控制"); h.add(effectController, "kd", 0.0, 1.0, 0.025).name("Kd"); h.add(effectController, "scale", 0.01, 1.0, 0.025).name("函數縮放"); // 材質(顏色) h = gui.addFolder("材質顏色"); h.add(effectController, "hue", 0.0, 1.0, 0.025).name("色相"); h.add(effectController, "saturation", 0.0, 1.0, 0.025).name("飽和度"); h.add(effectController, "lightness", 0.0, 1.0, 0.025).name("明度"); // 光源(顏色) h = gui.addFolder("光源顏色"); h.add(effectController, "lhue", 0.0, 1.0, 0.025).name("色相"); h.add(effectController, "lsaturation", 0.0, 1.0, 0.025).name("飽和度"); h.add(effectController, "llightness", 0.0, 1.0, 0.025).name("明度"); // 光源(方向) h = gui.addFolder("光源方向"); h.add(effectController, "lx", -1.0, 1.0, 0.025).name("X"); h.add(effectController, "ly", -1.0, 1.0, 0.025).name("Y"); h.add(effectController, "lz", -1.0, 1.0, 0.025).name("Z"); } document.addEventListener("DOMContentLoaded", function () { try { init(); fillScene(); setupGui(); addToDOM(); animate(); } catch(e) { var errorReport = "你的程式遇到不可復原的錯誤,無法繪製 Canvas。錯誤是:<br/><br/>"; $('#container').append(errorReport+e); } })