[webGL学习技术分享]基于three.js构建WebGL实例

Posted 码疯窝编程君

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[webGL学习技术分享]基于three.js构建WebGL实例相关的知识,希望对你有一定的参考价值。


今天,我们将继续学习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 scenethis.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 scenefunction animate() {

   requestAnimationFrame(animate);    render();    update();

}
// Update controls and statsfunction update() {

   lesson5.controls.update(lesson5.clock.getDelta());    lesson5.stats.update();

}
// Render the scenefunction render() {

   if (lesson5.renderer) {        lesson5.renderer.render(lesson5.scene, lesson5.camera);    }

}
// Initialize lesson on page loadfunction 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 ShaderMaterialvar 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); }

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 ); }

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 texturesvar 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 scenevar 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, cubeResolutionthis.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);}

    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 camerathis.bSphereCamera = new THREE.CubeCamera(0.1, 1000, 1000);    this.scene.add(this.bSphereCamera);    
      // prepare custom ShaderMaterialvar 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 shadervar customMaterial = new THREE.ShaderMaterial({    uniforms:       uniforms,    vertexShader:   document.getElementById('bubble-vertex').textContent,    fragmentShader: document.getElementById('bubble-fragment').textContent    });    
      // create spherical meshthis.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实例的主要内容,如果未能解决你的问题,请参考以下文章

      three.js学习笔记 之一(ITAEM团队学习分享)

      cesium 和 Three.js有啥区别,以及二者与WebGL 的关系

      WebGL/Three.js深度学习课程详解

      Web3D|基于WebGL的Three.js框架|入门篇

      WebGL和three.js的关系是啥样的?

      S170WebGL和Three.js精通课程深度解析一门通2019高清完整资源