[webGL学习]基于three.js构建WebGL实例第五讲
2016-10-11 17:20
591 查看
今天,我们将继续学习webgl(three.js)这门课程,今天我们将向您展示如何以三种不同的方式为您的场景创建一个美丽的环境(天空立体景象):立方天空盒 侧面),球形天空盒(单周围纹理)和球形着色器天空盒(无纹理)。 我们会用到下面一些比较特殊的属性:反射,折射和类似soapbubble的对象。
一般结构
现在我们可以开始,首先,定义一般结构:var lesson5 = { scene: null, camera: null, renderer: null, container: null, controls: null, clock: null, stats: null, init: function() { // Initialization // create main scene this.scene = new THREE.Scene(); var SCREEN_WIDTH = window.innerWidth, SCREEN_HEIGHT = window.innerHeight; // prepare camera var VIEW_ANGLE = 45, ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT, NEAR = 1, FAR = 1000; this.camera = new THREE.PerspectiveCamera( VIEW_ANGLE, ASPECT, NEAR, FAR); this.scene.add(this.camera); this.camera.position.set(0, 30, 150); this.camera.lookAt(new THREE.Vector3(0,0,0)); // prepare renderer this.renderer = new THREE.WebGLRenderer({antialias:true, alpha: false}); this.renderer.setSize(SCREEN_WIDTH, SCREEN_HEIGHT); this.renderer.setClearColor(0xffffff); this.renderer.shadowMapEnabled = true; this.renderer.shadowMapSoft = true; // prepare container this.container = document.createElement('div'); document.body.appendChild(this.container); this.container.appendChild(this.renderer.domElement); // events THREEx.WindowResize(this.renderer, this.camera); // prepare controls (OrbitControls) this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement); this.controls.target = new THREE.Vector3(0, 0, 0); this.controls.maxDistance = 700; // prepare clock this.clock = new THREE.Clock(); // prepare stats this.stats = new Stats(); this.stats.domElement.style.position = 'absolute'; this.stats.domElement.style.left = '50px'; this.stats.domElement.style.bottom = '50px'; this.stats.domElement.style.zIndex = 1; this.container.appendChild( this.stats.domElement ); // add point light var spLight = new THREE.PointLight(0xffffff, 1.75, 1000); spLight.position.set(-100, 200, 200); this.scene.add(spLight); // add simple cube var cube = new THREE.Mesh( new THREE.CubeGeometry(50, 10, 50), new THREE.MeshLambertMaterial({color:0xffffff * Math.random()}) ); cube.position.set(0, 0, 0); this.scene.add(cube); // add custom objects // ..... } }; // Animate the scene function animate() { requestAnimationFrame(animate); render(); update(); } // Update controls and stats function update() { lesson5.controls.update(lesson5.clock.getDelta()); lesson5.stats.update(); } // Render the scene function render() { if (lesson5.renderer) { lesson5.renderer.render(lesson5.scene, lesson5.camera); } } // Initialize lesson on page load function initializeLesson() { lesson5.init(); animate(); } if (window.addEventListener) window.addEventListener('load', initializeLesson, false); else if (window.attachEvent) window.attachEvent('onload', initializeLesson); else window.onload = initializeLesson;
这是非常常见的结构,添加了所有的一般元素,如:scene本身,camera,render,controls,light和stats元素。 现在我们将要开始描述每个天空盒类型。
Part 1: Skyboxes
1. Cubic Skybox (textured)
drawSimpleSkybox: function() { // define path and box sides images var path = 'skybox/1/'; var sides = [ path + 'sbox_px.jpg', path + 'sbox_nx.jpg', path + 'sbox_py.jpg', path + 'sbox_ny.jpg', path + 'sbox_pz.jpg', path + 'sbox_nz.jpg' ]; // load images var scCube = THREE.ImageUtils.loadTextureCube(sides); scCube.format = THREE.RGBFormat; // prepare skybox material (shader) var skyShader = THREE.ShaderLib["cube"]; skyShader.uniforms["tCube"].value = scCube; var skyMaterial = new THREE.ShaderMaterial( { fragmentShader: skyShader.fragmentShader, vertexShader: skyShader.vertexShader, uniforms: skyShader.uniforms, depthWrite: false, side: THREE.BackSide }); // create Mesh with cube geometry and add to the scene var skyBox = new THREE.Mesh(new THREE.CubeGeometry(500, 500, 500), skyMaterial); skyMaterial.needsUpdate = true; this.scene.add(skyBox); }
最简单的 - 是创建基本的立方天空盒子。 在three.js的utils类中有一个特殊的函数来加载图像集合:ImageUtils :: loadTextureCube。 然后,我们使用THREE.ShaderLib为我们下面的立方天空盒创建ShaderMaterial(这个使用CubeGeometry类)。
2. Spherical skybox (textured)
除了标准的方式,天空场景可以是球面的,在这种情况下,我们可以只使用一个球面纹理的来修饰天空:drawSphericalSkybox: function() { // prepare ShaderMaterial var uniforms = { texture: { type: 't', value: THREE.ImageUtils.loadTexture('skybox/2/skybox.jpg') } }; var skyMaterial = new THREE.ShaderMaterial( { uniforms: uniforms, vertexShader: document.getElementById('sky-vertex').textContent, fragmentShader: document.getElementById('sky-fragment').textContent }); // create Mesh with sphere geometry and add to the scene var skyBox = new THREE.Mesh(new THREE.SphereGeometry(250, 60, 40), skyMaterial); skyBox.scale.set(-1, 1, 1); skyBox.eulerOrder = 'XZY'; skyBox.renderDepth = 500.0; this.scene.add(skyBox); }
注意,为了构建它,我们使用特殊着色器:天顶点和天空碎片:
<!-- skybox shaders --> <script type="application/x-glsl" id="sky-vertex"> varying vec2 vUV; void main() { vUV = uv; vec4 pos = vec4(position, 1.0); gl_Position = projectionMatrix * modelViewMatrix * pos; } </script> <script type="application/x-glsl" id="sky-fragment"> uniform sampler2D texture; varying vec2 vUV; void main() { vec4 sample = texture2D(texture, vUV); gl_FragColor = vec4(sample.xyz, sample.w); } </script> <!-- /skybox shaders -->
因此,我们的球形天空盒有一个类似的结构,但我们使用另一个几何(SphereGeometry)和新的着色材料.
3. Spherical skybox (non textured)
有时,清晰的天空就足够了,在这种情况下,我们不需要使用额外的图像来构建天空盒。 在这里,你可以找到我们如何避免使用纹理来构建漂亮的球形天空盒与渐变。 首先,我们需要添加两个新的着色器:<!-- skybox shaders --> <script type="x-shader/x-vertex" id="sky-vertex"> varying vec3 vWorldPosition; void main() { vec4 worldPosition = modelMatrix * vec4( position, 1.0 ); vWorldPosition = worldPosition.xyz; gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); } </script> <script type="x-shader/x-fragment" id="sky-fragment"> uniform vec3 topColor; uniform vec3 bottomColor; uniform float offset; uniform float exponent; varying vec3 vWorldPosition; void main() { float h = normalize( vWorldPosition + offset ).y; gl_FragColor = vec4( mix( bottomColor, topColor, max( pow( h, exponent ), 0.0 ) ), 1.0 ); } </script> <!-- /skybox shaders -->
然后,实现基于渐变的skybox:
drawShaderSkybox: function() { // prepare ShaderMaterial without textures var vertexShader = document.getElementById('sky-vertex').textContent, fragmentShader = document.getElementById('sky-fragment').textContent; var uniforms = { topColor: {type: "c", value: new THREE.Color(0x0055ff)}, bottomColor: {type: "c", value: new THREE.Color(0xffffff)}, offset: {type: "f", value: 50}, exponent: {type: "f", value: 0.6} } var skyMaterial = new THREE.ShaderMaterial({vertexShader: vertexShader, fragmentShader: fragmentShader, uniforms: uniforms, side: THREE.BackSide, fog: false}); // create Mesh with sphere geometry and add to the scene var skyBox = new THREE.Mesh( new THREE.SphereGeometry(250, 60, 40), skyMaterial); this.scene.add(skyBox); }
在例子中 - 我们没有使用图像,我们只使用两种颜色的渐变:顶部和底部的颜色。
Part 2: Additional objects
正如你可能已经注意到 - 这里,使用各种视觉效果:反射,折射和气泡。 在这一节,将向大家展示一下具体的实现过程。1. Reflection
你可以使用这个方法(事实上 - 函数)来构建反射面:drawReflectingObjects: function() { // Object 1: rectangle // create additional camera this.mCubeCamera = new THREE.CubeCamera(0.1, 1000, 1000); // near, far, cubeResolution this.scene.add(this.mCubeCamera); // create mirror material and mesh var mirrorCubeMaterial = new THREE.MeshBasicMaterial( { envMap: this.mCubeCamera.renderTarget, side: THREE.DoubleSide } ); this.mCube = new THREE.Mesh( new THREE.CubeGeometry(100, 100, 5, 1, 1, 1), mirrorCubeMaterial); this.mCube.position.set(-50, 0, -150); this.mCubeCamera.position = this.mCube.position; this.mCubeCamera.lookAt(new THREE.Vector3(0, 0, 0)); this.scene.add(this.mCube); // Object 2: sphere // create additional camera this.mSphereCamera = new THREE.CubeCamera(0.1, 1000, 100); this.scene.add(this.mSphereCamera); // create mirror material and mesh var mirrorSphereMaterial = new THREE.MeshBasicMaterial( { envMap: this.mSphereCamera.renderTarget, side: THREE.DoubleSide } ); this.mSphere = new THREE.Mesh( new THREE.SphereGeometry(50, 32, 32), mirrorSphereMaterial ); this.mSphere.position.set(50, 0, -150); this.mSphereCamera.position = this.mSphere.position; this.mSphereCamera.lookAt(new THREE.Vector3(0, 0, 0)); this.scene.add(this.mSphere); }
一般来说 - 这很容易实现 - 我们使用两个对象(矩形和球体)与基本材料。 但这里是一个小技巧 - 我们在我们的材料中使用了envMap。 此属性允许设置材料的环境贴图。 刚刚上面我们创造了两个摄像机,并把它放在相同的位置,我们的立方体和球体。 这就是为什么他们反映。 然而,有一个时刻 - 每次我们移动我们的主要相机 - 我们需要更新我们的相机,因此我们需要添加以下代码到’render’这个函数:
// Render the scene function render() { if (lesson5.renderer) { // update reflecting objects lesson5.mCube.visible = false; lesson5.mCubeCamera.updateCubeMap(lesson5.renderer, lesson5.scene); lesson5.mCube.visible = true; lesson5.mSphere.visible = false; lesson5.mSphereCamera.updateCubeMap(lesson5.renderer, lesson5.scene); lesson5.mSphere.visible = true; lesson5.renderer.render(lesson5.scene, lesson5.camera); } }
2. Refraction
你可以使用这个方法(事实上 - 函数)来创建折射对象:drawRefractingObject: function() { // create additional camera this.rSphereCamera = new THREE.CubeCamera(0.1, 1000, 1000); this.scene.add(this.rSphereCamera); this.rSphereCamera.renderTarget.mapping = new THREE.CubeRefractionMapping(); // create refracting material and spherical mesh var rMaterial = new THREE.MeshBasicMaterial({ color: 0xffffdd, envMap: this.rSphereCamera.renderTarget, refractionRatio: 0.995, reflectivity: 0.5 }); this.rSphere = new THREE.Mesh( new THREE.SphereGeometry(40, 32, 32), rMaterial); this.rSphere.position.set(0, 0, 100); this.rSphereCamera.position = this.rSphere.position; this.scene.add(this.rSphere); }
我们使用几乎相同的方法,我们用于反射。 我们只为我们的材料添加了两个新属性:refractionRatio和reflectivity。 样式为我们的球体添加折射效果,现在看起来像放大镜。 请记住,我们需要在’渲染’功能中更新我们的相机:
// Render the scene function render() { if (lesson5.renderer) { // update refracting object lesson5.rSphere.visible = false; lesson5.rSphereCamera.updateCubeMap(lesson5.renderer, lesson5.scene); lesson5.rSphere.visible = true; lesson5.renderer.render(lesson5.scene, lesson5.camera); } }
3. Bubble
<!-- bubble shaders --> <script type="x-shader/x-vertex" id="bubble-vertex"> uniform float mRefractionRatio; uniform float mBias; uniform float mScale; uniform float mPower; varying vec3 vReflect; varying vec3 vRefract[3]; varying float vReflectionFactor; void main() { vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 ); vec4 worldPosition = modelMatrix * vec4( position, 1.0 ); vec3 worldNormal = normalize( mat3( modelMatrix[0].xyz, modelMatrix[1].xyz, modelMatrix[2].xyz ) * normal ); vec3 I = worldPosition.xyz - cameraPosition; vReflect = reflect( I, worldNormal ); vRefract[0] = refract( normalize( I ), worldNormal, mRefractionRatio ); vRefract[1] = refract( normalize( I ), worldNormal, mRefractionRatio * 0.99 ); vRefract[2] = refract( normalize( I ), worldNormal, mRefractionRatio * 0.98 ); vReflectionFactor = mBias + mScale * pow( 1.0 + dot( normalize( I ), worldNormal ), mPower ); gl_Position = projectionMatrix * mvPosition; } </script> <script type="x-shader/x-fragment" id="bubble-fragment"> uniform samplerCube tCube; varying vec3 vReflect; varying vec3 vRefract[3]; varying float vReflectionFactor; void main() { vec4 reflectedColor = textureCube( tCube, vec3( -vReflect.x, vReflect.yz ) ); vec4 refractedColor = vec4( 1.0 ); refractedColor.r = textureCube( tCube, vec3( -vRefract[0].x, vRefract[0].yz ) ).r; refractedColor.g = textureCube( tCube, vec3( -vRefract[1].x, vRefract[1].yz ) ).g; refractedColor.b = textureCube( tCube, vec3( -vRefract[2].x, vRefract[2].yz ) ).b; gl_FragColor = mix( refractedColor, reflectedColor, clamp( vReflectionFactor, 0.0, 1.0 ) ); } </script> <!-- /bubble shaders -->
这里是新的着色材料与THREE.Cube相机为我们的对象:
drawBubbleObject: function() { // create additional camera this.bSphereCamera = new THREE.CubeCamera(0.1, 1000, 1000); this.scene.add(this.bSphereCamera); // prepare custom ShaderMaterial var uniforms = { "mRefractionRatio": { type: "f", value: 1.02 }, "mBias": { type: "f", value: 0.1 }, "mPower": { type: "f", value: 2.0 }, "mScale": { type: "f", value: 1.0 }, "tCube": { type: "t", value: this.bSphereCamera.renderTarget } // textureCube } }; // create custom material for the shader var customMaterial = new THREE.ShaderMaterial({ uniforms: uniforms, vertexShader: document.getElementById('bubble-vertex').textContent, fragmentShader: document.getElementById('bubble-fragment').textContent }); // create spherical mesh this.bSphere = new THREE.Mesh( new THREE.SphereGeometry(50, 32, 32), customMaterial); this.bSphere.position.set(-75, 0, 0); this.scene.add(this.bSphere); this.bSphereCamera.position = this.bSphere.position; }
再次,我们需要更新您的额外的相机所有的时间:
// Render the scene function render() { if (lesson5.renderer) { // update bubble object lesson5.bSphere.visible = false; lesson5.bSphereCamera.updateCubeMap(lesson5.renderer, lesson5.scene); lesson5.bSphere.visible = true; lesson5.renderer.render(lesson5.scene, lesson5.camera); } }
结束
未完继续源码下载请关注我的微信公众号
相关文章推荐
- [webGL学习]基于three.js构建WebGL实例第六讲
- [webGL学习]基于three.js构建WebGL实例第二讲
- [webGL学习]基于three.js构建WebGL实例第一讲
- [webGL学习]基于three.js构建WebGL实例第三讲
- [webGL学习]基于three.js构建WebGL实例第一讲
- [webGL学习]基于three.js构建WebGL实例第四讲
- [webGL学习]基于three.js构建WebGL实例讲解大纲
- webgl+three.js,学习笔记,画一个立方体+解析
- 小八——WebGL心路历程(2),一个基于Three.js的webGL程序解析
- webgl+three.js,学习笔记,调用摄像头,做成纹理
- Three.js讲解以及WEBGL实例(转)
- Three.js讲解以及WEBGL实例
- 学习笔记Webgl之Three.js (1)
- Three.JS-学习 The WebGL earth (1)---globe.html 分析
- Three.JS提升学习2:构建Three.js场景的基本组件
- 学习 WebGL 的开源库 Three.js
- WebGL之ThreeJS学习之旅01
- Three.js讲解以及WEBGL实例
- three.js学习笔记 基于物理引擎的第一人称控件
- 20个令人印象深刻的例子学习Three.js实现WebGL