WebGL:在 WebGL 中缩放到场景中的对象并在其处停止 [英] WebGL: Zooming to and stopping at object in a scene in WebGL

查看:23
本文介绍了WebGL:在 WebGL 中缩放到场景中的对象并在其处停止的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们创建了一个 WebGl 应用程序,该应用程序显示包含多个对象的场景.整个场景可以向多个方向旋转.该应用程序要求用户能够放大但不能通过对象.我知道可以使用诸如 Three.js 和 SceneJs 之类的 webgl 框架来实现此功能.不幸的是,我们的应用程序没有利用框架.有没有办法仅使用 webgl 来实现此处描述的缩放功能?注意:我不相信对象选择对我们有用,因为用户不需要选择场景中的任何对象.感谢您的帮助.

We've created a WebGl application which displays a scene containing multiple objects. The entire scene can be rotated in multiple directions. The application requires the user to be able to zoom up to but NOT thru the object. I know this functionality can be implemented using webgl frameworks such as Three.js and SceneJs. Unfortunately, our application is not leveraging a framework. Is there a way to implement the zoom functionality described here using webgl only? Note: I don't believe object picking will work for us since the user is not required to select any object in the scene. Thanks for your help.

推荐答案

让我头疼.

首先,您需要知道世界空间中每个对象的大小.例如,如果一个对象有 10 个单位大而另一个对象有 100 个单位大,您可能希望与 100 个单位的对象的距离与 10 个单位的对象不同.世界空间我的意思是,如果您将 10 个单位的对象缩放 9,那么在世界空间中它将是 90 个单位,如果它是 10 个单位,那么您又希望获得不同的距离

First off you need to know the size of each object in world space. For example if one object is 10 units big and another is 100 units big you probably want to be a different distance from the 100 unit object as the 10 unit object. By world space I also mean if you're scaling the 10 unit object by 9 then in world space it would be 90 units big and again you'd want to get a different distance away then if it was 10 units

您通常通过计算其顶点的范围来计算局部空间中对象的大小.只需遍历所有顶点并跟踪 x、y 和 z 中的最小值和最大值.您是想从对象的原点获取最大的值还是计算实际的中心点取决于您.

You generally compute the size of an object in local space by computing the extents of its vertices. Just go through all the vertices and keep track of the min and max values in x, y, and z. Whether you want to take the biggest value from the object's origin or compute an actual center point is up to you.

因此,考虑到尺寸,我们可以计算出需要多远才能看到整个物体.对于标准透视矩阵,您可以向后工作.如果你知道你的物体有 10 个单位大,那么你需要在你的平截头体中放置 10 个单位.您实际上可能会选择 14 个单位(比如大小 * 1.4),以便对象周围有一些空间.

So, given the size we can compute how far away you need to be to see the entire object. For the standard perspective matrix you can just work backward. If you know your object is 10 units big then you need to fit 10 units in your frustum. You'd probably actually pick something like 14 units (say size * 1.4) so there's some space around the object.

我们知道halfFovyhalfSizeToFitOnScreen,我们需要计算distance

We know halfFovy, halfSizeToFitOnScreen, we need to compute distance

sohcahtoa
tangent = opposite / adjacent
opposite = halfsizeToFitOnScreen
adjacent = distance
tangent  = Math.tan(halfFovY)

因此

tangent = sizeToFitOnScreen / distance
tangent * distance = sizeToFitOnScreen
distance = sizeToFitOnScreen / tangent
distance = sizeToFitOnScreen / Math.tan(halfFovY)

所以现在我们知道相机需要距离物体distance.有一整个球体距离物体 distance .您选择该球体的位置取决于您.假设您从相机当前所在的位置出发,您可以计算从物体到相机的方向

So now we know the camera needs to be distance away from the object. There's an entire sphere that's distance away from the object. Where you pick on that sphere is up to you. Assuming you go from where the camera currently is you can compute the direction from the object to the camera

direction = normalize(cameraPos - objectPos)

现在您可以计算在那个方向上距离的一个点.

Now you can compute a point distance away in that direction.

desiredCameraPosition = direction * distance

现在要么使用一些lookAt函数将相机放在那里

Now either put the camera there using some lookAt function

matrix = lookAt(desiredCameraPosition, objectPosition, up)

或在相机当前所在位置与新的所需位置之间进行 lerp

Or lerp between where the camera currently is to it's new desired position

var m4 = twgl.m4;
    var v3 = twgl.v3;
    twgl.setAttributePrefix("a_");
    var gl = twgl.getWebGLContext(document.getElementById("c"));
    var programInfo = twgl.createProgramInfo(gl, ["vs", "fs"]);

    var shapes = [
      twgl.primitives.createCubeBufferInfo(gl, 2),
      twgl.primitives.createSphereBufferInfo(gl, 1, 24, 12),
      twgl.primitives.createTruncatedConeBufferInfo(gl, 1, 0, 2, 24, 1),
    ];

    function rand(min, max) {
      return min + Math.random() * (max - min);
    }

    function easeInOut(t, start, end) {
      var c = end - start;
      if ((t /= 0.5) < 1) {
        return c / 2 * t * t + start;
      } else {
        return -c / 2 * ((--t) * (t - 2) - 1) + start;
      }
    }

    // Shared values
    var lightWorldPosition = [1, 8, -10];
    var lightColor = [1, 1, 1, 1];
    var camera = m4.identity();
    var view = m4.identity();
    var viewProjection = m4.identity();
    var targetNdx = 0;
    var targetTimer = 0;
    var zoomTimer = 0;
    var eye = v3.copy([1, 4, -60]);
    var target = v3.copy([0, 0, 0]);
    var up = [0, 1, 0];
    var zoomScale = 1.4;
    var zoomDuration = 2;
    var targetChangeInterval = 3;
    var oldEye;
    var oldTarget;
    var newEye;
    var newTarget;

    var tex = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, tex);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 2, 2, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([
      255,255,255,255,
      192,192,192,255,
      192,192,192,255,
      255,255,255,255]));
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);

    var objects = [];
    var drawObjects = [];
    var numObjects = 100;
    var baseHue = rand(0, 360);
    for (var ii = 0; ii < numObjects; ++ii) {
      var uniforms = {
        u_lightWorldPos: lightWorldPosition,
        u_lightColor: lightColor,
        u_diffuseMult: chroma.hsv((baseHue + rand(0, 60)) % 360, 0.4, 0.8).gl(),
        u_specular: [1, 1, 1, 1],
        u_shininess: 50,
        u_specularFactor: 1,
        u_diffuse: tex,
        u_viewInverse: camera,
        u_world: m4.identity(),
        u_worldInverseTranspose: m4.identity(),
        u_worldViewProjection: m4.identity(),
      };
      drawObjects.push({
        programInfo: programInfo,
        bufferInfo: shapes[ii % shapes.length],
        uniforms: uniforms,
      });
      objects.push({
        translation: [rand(-50, 50), rand(-50, 50), rand(-50, 50)],
        scale: rand(1, 5),
        size: 2,
        xSpeed: rand(0.2, 0.7),
        zSpeed: rand(0.2, 0.7),
        uniforms: uniforms,
      });
    }

    var then = Date.now() * 0.001;

    function render() {
      twgl.resizeCanvasToDisplaySize(gl.canvas);
      gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);

      gl.enable(gl.DEPTH_TEST);
      gl.enable(gl.CULL_FACE);
      gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

      var time = Date.now() * 0.001;
      var elapsed = time - then;
      then = time;

      var radius = 6;
      var fovy = 30 * Math.PI / 180;
      var projection = m4.perspective(fovy, gl.canvas.clientWidth / gl.canvas.clientHeight, 0.5, 200);

      targetTimer -= elapsed;
      if (targetTimer <= 0) {
        targetTimer = targetChangeInterval;
        zoomTimer = 0;
        targetNdx = (targetNdx + 1) % objects.length;
        oldEye = v3.copy(eye);
        oldTarget = v3.copy(target);
        var targetObj = objects[targetNdx];
        newTarget = targetObj.translation;
        var halfSize = targetObj.size * targetObj.scale * zoomScale * 0.5;
        var distance = halfSize / Math.tan(fovy * 0.5);
        var direction = v3.normalize(v3.subtract(eye, newTarget));
        newEye = v3.add(newTarget, v3.mulScalar(direction, distance));
      }

      zoomTimer += elapsed;
      var lerp = easeInOut(Math.min(1, zoomTimer / zoomDuration), 0, 1);
      eye = v3.lerp(oldEye, newEye, lerp);
      target = v3.lerp(oldTarget, newTarget, lerp);

      m4.lookAt(eye, target, up, camera);
      m4.inverse(camera, view);
      m4.multiply(projection, view, viewProjection);

      objects.forEach(function(obj, ndx) {
        var uni = obj.uniforms;
        var world = uni.u_world;
        m4.identity(world);
        m4.translate(world, obj.translation, world);
        m4.rotateX(world, time * obj.xSpeed, world);
        m4.rotateZ(world, time * obj.zSpeed, world);
        m4.scale(world, [obj.scale, obj.scale, obj.scale], world);
        m4.transpose(m4.inverse(world, uni.u_worldInverseTranspose), uni.u_worldInverseTranspose);
        m4.multiply(viewProjection, uni.u_world, uni.u_worldViewProjection);
      });

      twgl.drawObjectList(gl, drawObjects);

      requestAnimationFrame(render);
    }
    render();

body {
  margin: 0;
}
canvas {
  width: 100vw;
  height: 100vh;
  display: block;
}

<script src="//twgljs.org/dist/4.x/twgl-full.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/chroma-js/0.6.3/chroma.min.js"></script>
<canvas id="c"></canvas>
<script id="vs" type="notjs">
uniform mat4 u_worldViewProjection;
uniform vec3 u_lightWorldPos;
uniform mat4 u_world;
uniform mat4 u_viewInverse;
uniform mat4 u_worldInverseTranspose;

attribute vec4 a_position;
attribute vec3 a_normal;
attribute vec2 a_texcoord;

varying vec4 v_position;
varying vec2 v_texCoord;
varying vec3 v_normal;
varying vec3 v_surfaceToLight;
varying vec3 v_surfaceToView;

void main() {
  v_texCoord = a_texcoord;
  v_position = (u_worldViewProjection * a_position);
  v_normal = (u_worldInverseTranspose * vec4(a_normal, 0)).xyz;
  v_surfaceToLight = u_lightWorldPos - (u_world * a_position).xyz;
  v_surfaceToView = (u_viewInverse[3] - (u_world * a_position)).xyz;
  gl_Position = v_position;
}
  </script>
  <script id="fs" type="notjs">
precision mediump float;

varying vec4 v_position;
varying vec2 v_texCoord;
varying vec3 v_normal;
varying vec3 v_surfaceToLight;
varying vec3 v_surfaceToView;

uniform vec4 u_lightColor;
uniform vec4 u_diffuseMult;
uniform sampler2D u_diffuse;
uniform vec4 u_specular;
uniform float u_shininess;
uniform float u_specularFactor;

vec4 lit(float l ,float h, float m) {
  return vec4(1.0,
              abs(l),//max(l, 0.0),
              (l > 0.0) ? pow(max(0.0, h), m) : 0.0,
              1.0);
}

void main() {
  vec4 diffuseColor = texture2D(u_diffuse, v_texCoord) * u_diffuseMult;
  vec3 a_normal = normalize(v_normal);
  vec3 surfaceToLight = normalize(v_surfaceToLight);
  vec3 surfaceToView = normalize(v_surfaceToView);
  vec3 halfVector = normalize(surfaceToLight + surfaceToView);
  vec4 litR = lit(dot(a_normal, surfaceToLight),
                    dot(a_normal, halfVector), u_shininess);
  vec4 outColor = vec4((
  u_lightColor * (diffuseColor * litR.y +
                u_specular * litR.z * u_specularFactor)).rgb,
      diffuseColor.a);
  gl_FragColor = outColor;
}
  </script>

这篇关于WebGL:在 WebGL 中缩放到场景中的对象并在其处停止的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆