[webGL学习]基于three.js构建WebGL实例第五讲

来源:互联网 发布:win10自带软件 编辑:程序博客网 时间:2024/06/05 17:22

这里写图片描述

今天,我们将继续学习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 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 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 scenefunction 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 scenefunction 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 scenefunction 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);    }}

结束

未完继续
源码下载请关注我的微信公众号
这里写图片描述

1 0
原创粉丝点击