【问题标题】:three.js: BufferGeometry with texture coordinatesthree.js:带有纹理坐标的BufferGeometry
【发布时间】:2020-06-22 13:59:28
【问题描述】:

我找不到带有纹理坐标的 THREE.BufferGeometry 示例。它应该用于纹理网格吗?我无法让它工作。这是我的测试代码:

var quad_vertices =
[
    -30.0,  30.0, 0.0,
     30.0,  30.0, 0.0,
     30.0, -30.0, 0.0,
    -30.0, -30.0, 0.0
];

var quad_uvs =
[
    0.0, 0.0,
    1.0, 0.0,
    1.0, 1.0,
    0.0, 1.0
];

var quad_indices =
[
    0, 2, 1, 0, 3, 2
];

var geometry = new THREE.BufferGeometry();

geometry.attributes =
{
    position:
    {
        itemSize: 3,
        array: new Float32Array(3 * 4)
    },

    uv:
    {
        itemSize: 2,
        array: new Float32Array(2 * 4)
    },

    index:
    {
        itemSize: 1,
        array: new Uint16Array(6)
    }
};

var positions = geometry.attributes.position.array;
var uvs       = geometry.attributes.uv.array;
var indices   = geometry.attributes.index.array;

var i;
for(i = 0; i < positions.length; i += 3)
{
    positions[i]     = quad_vertices[i];
    positions[i + 1] = quad_vertices[i + 1];
    positions[i + 2] = quad_vertices[i + 2];
}

for(i = 0; i < uvs.length; i += 2)
{
    uvs[i]     = quad_uvs[i];
    uvs[i + 1] = quad_uvs[i + 1];
}

for(i = 0; i < indices.length; i++)
    indices[i] = quad_indices[i];

var texture = THREE.ImageUtils.loadTexture('./assets/texture.png');
texture.anisotropy = renderer.getMaxAnisotropy();

var material = new THREE.MeshBasicMaterial( { map: texture } );

var mesh = new THREE.Mesh(geometry, material);

mesh.position.z = -100;

scene.add(mesh);

只需使用 THREE.Geometry 创建网格就可以了,所以我不知道这段代码有什么问题。有什么想法吗?

【问题讨论】:

  • THREE.BufferGeometryUtils.fromGeometry( geometry, settings )。此外,three.js 不再支持四边形。更新到 r.61。
  • 我不必从 Geometry 创建 BufferGeometry,对吧?所以这只不过是一种解决方法。我也没有使用 three.js quads。
  • 你没有,但如果你使用过该函数一次,你至少会看到BufferGeometry是如何创建的。
  • 即使在阅读 BufferGeometryUtils.js 之后也看不出我的代码有什么问题 似乎没有创建索引,但这有关系吗?它们是在threejs.org/examples/#webgl_buffergeometry 中创建的,一切正常。但不是我的代码。根本没有渲染任何东西,甚至没有一些损坏的几何图形。
  • 我认为您可能需要为几何图形创建偏移量。在这里查看我的问题和答案:stackoverflow.com/questions/19613281/…

标签: three.js


【解决方案1】:

这是一个带有 uvs 的索引 BufferGeometry 的工作示例。我更新了您的示例以使用three.js r83。我看到旧代码有两个问题。首先,您不能只将 geometry.attributes 设置为 JSON 对象定义。 THREE.BufferAttribute 是一个类,但您的 JSON 缺少 THREE.Renderer 所需的原型上的函数定义。第二个 THREE.ImageUtils 已被 THREE.TextureLoader 替换,因此我也在示例中对其进行了更新。

var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );

var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );

var quad_vertices =
[
-30.0,  30.0, 0.0,
30.0,  30.0, 0.0,
30.0, -30.0, 0.0,
-30.0, -30.0, 0.0
];

var quad_uvs =
[
0.0, 0.0,
1.0, 0.0,
1.0, 1.0,
0.0, 1.0
];

var quad_indices =
[
0, 2, 1, 0, 3, 2
];

var geometry = new THREE.BufferGeometry();

var vertices = new Float32Array( quad_vertices );
// Each vertex has one uv coordinate for texture mapping
var uvs = new Float32Array( quad_uvs);
// Use the four vertices to draw the two triangles that make up the square.
var indices = new Uint32Array( quad_indices )

// itemSize = 3 because there are 3 values (components) per vertex
geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
geometry.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) );
geometry.setIndex( new THREE.BufferAttribute( indices, 1 ) );

// Load the texture asynchronously
var textureLoader = new THREE.TextureLoader();
textureLoader.load('./assets/texture.jpg', function (texture){
console.log('texture loaded');

var material = new THREE.MeshBasicMaterial( {map: texture });
var mesh = new THREE.Mesh( geometry, material );
mesh.position.z = -100;

scene.add(mesh);

renderer.render(scene, camera);
}, undefined, function (err) {
console.error('texture not loaded', err)
});

进一步参考:

Creating a scene

BufferAttribute

【讨论】:

    【解决方案2】:

    对于那些希望将索引缓冲区几何体与纹理和自定义着色器材质(我相信这接近性能上限)相结合的人,我使用了以下方法。所有实际工作都发生在loadImage() 以及顶点和片段着色器中,其余的只是设置 Three.js(版本 92)的样板:

    /**
    * Generate a scene object with a background color
    **/
    
    function getScene() {
      var scene = new THREE.Scene();
      scene.background = new THREE.Color(0xffffff);
      return scene;
    }
    
    /**
    * Generate the camera to be used in the scene. Camera args:
    *   [0] field of view: identifies the portion of the scene
    *     visible at any time (in degrees)
    *   [1] aspect ratio: identifies the aspect ratio of the
    *     scene in width/height
    *   [2] near clipping plane: objects closer than the near
    *     clipping plane are culled from the scene
    *   [3] far clipping plane: objects farther than the far
    *     clipping plane are culled from the scene
    **/
    
    function getCamera() {
      var aspectRatio = window.innerWidth / window.innerHeight;
      var camera = new THREE.PerspectiveCamera(75, aspectRatio, 0.1, 1000);
      camera.position.set(0, 1, 10);
      return camera;
    }
    
    /**
    * Generate the renderer to be used in the scene
    **/
    
    function getRenderer() {
      // Create the canvas with a renderer
      var renderer = new THREE.WebGLRenderer({antialias: true});
      // Add support for retina displays
      renderer.setPixelRatio(window.devicePixelRatio);
      // Specify the size of the canvas
      renderer.setSize(window.innerWidth, window.innerHeight);
      // Add the canvas to the DOM
      document.body.appendChild(renderer.domElement);
      return renderer;
    }
    
    /**
    * Generate the controls to be used in the scene
    * @param {obj} camera: the three.js camera for the scene
    * @param {obj} renderer: the three.js renderer for the scene
    **/
    
    function getControls(camera, renderer) {
      var controls = new THREE.TrackballControls(camera, renderer.domElement);
      controls.zoomSpeed = 0.4;
      controls.panSpeed = 0.4;
      return controls;
    }
    
    /**
    * Load image
    **/
    
    function loadImage() {
    
      var geometry = new THREE.BufferGeometry();
    
      /*
      Now we need to push some vertices into that geometry to identify the coordinates the geometry should cover
      */
    
      // Identify the image size
      var imageSize = {width: 10, height: 7.5};
    
      // Identify the x, y, z coords where the image should be placed
      var coords = {x: -5, y: -3.75, z: 0};
    
      // Add one vertex for each corner of the image, using the 
      // following order: lower left, lower right, upper right, upper left
      var vertices = new Float32Array([
        coords.x, coords.y, coords.z, // bottom left
        coords.x+imageSize.width, coords.y, coords.z, // bottom right
        coords.x+imageSize.width, coords.y+imageSize.height, coords.z, // upper right
        coords.x, coords.y+imageSize.height, coords.z, // upper left
      ])
    
      // set the uvs for this box; these identify the following corners:
      // lower-left, lower-right, upper-right, upper-left
      var uvs = new Float32Array([
        0.0, 0.0,
        1.0, 0.0,
        1.0, 1.0,
        0.0, 1.0,
      ])
    
      // indices = sequence of index positions in `vertices` to use as vertices
      // we make two triangles but only use 4 distinct vertices in the object
      // the second argument to THREE.BufferAttribute is the number of elements
      // in the first argument per vertex
      geometry.setIndex([0,1,2, 2,3,0])
      geometry.addAttribute('position', new THREE.BufferAttribute( vertices, 3 ));
      geometry.addAttribute('uv', new THREE.BufferAttribute( uvs, 2) )
    
      // Create a texture loader so we can load our image file
      var loader = new THREE.TextureLoader();
    
      // specify the url to the texture
      var url = 'https://s3.amazonaws.com/duhaime/blog/tsne-webgl/assets/cat.jpg';
    
      // specify custom uniforms and attributes for shaders
      // Uniform types: https://github.com/mrdoob/three.js/wiki/Uniforms-types
      var material = new THREE.ShaderMaterial({  
        uniforms: {
          texture: {
            type: 't',
            value: loader.load(url)
          },
        },
        vertexShader: document.getElementById('vertex-shader').textContent,
        fragmentShader: document.getElementById('fragment-shader').textContent
      });
    
      // Combine our image geometry and material into a mesh
      var mesh = new THREE.Mesh(geometry, material);
    
      // Set the position of the image mesh in the x,y,z dimensions
      mesh.position.set(0,0,0)
    
      // Add the image to the scene
      scene.add(mesh);
    }
    
    /**
    * Render!
    **/
    
    function render() {
      requestAnimationFrame(render);
      renderer.render(scene, camera);
      controls.update();
    };
    
    var scene = getScene();
    var camera = getCamera();
    var renderer = getRenderer();
    var controls = getControls(camera, renderer);
    loadImage();
    
    render();
    html, body { width: 100%; height: 100%; background: #000; }
    body { margin: 0; overflow: hidden; }
    canvas { width: 100%; height: 100%; }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/92/three.min.js"></script>
    <script src="https://threejs.org/examples/js/controls/TrackballControls.js"></script>
    <script type='x-shader/x-vertex' id='vertex-shader'>
      /**
      * The vertex shader's main() function must define `gl_Position`,
      * which describes the position of each vertex in the space.
      *
      * To do so, we can use the following variables defined by Three.js:        
      *   
      *   uniform mat4 modelViewMatrix - combines:
      *     model matrix: maps a point's local coordinate space into world space
      *     view matrix: maps world space into camera space
      *
      *   uniform mat4 projectionMatrix - maps camera space into screen space
      *
      *   attribute vec3 position - sets the position of each vertex
      *
      *   attribute vec2 uv - determines the relationship between vertices and textures
      *
      * `uniforms` are constant across all vertices
      *
      * `attributes` can vary from vertex to vertex and are defined as arrays
      * with length equal to the number of vertices. Each index in the array
      * is an attribute for the corresponding vertex
      *
      * `varyings` are values passed from the vertex to the fragment shader
      **/
    
      varying vec2 vUv; // pass the uv coordinates of each pixel to the frag shader
    
      void main() {
        vUv = uv;
        gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
      }
    </script>
    
    <script type='x-shader/x-fragment' id='fragment-shader'>
      /**
      * The fragment shader's main() function must define `gl_FragColor`,
      * which describes the pixel color of each pixel on the screen.
      *
      * To do so, we can use uniforms passed into the shader and varyings
      * passed from the vertex shader
      **/
    
      precision highp float; // set float precision (optional)
    
      uniform sampler2D texture; // identify the texture as a uniform argument
      varying vec2 vUv; // identify the uv values as a varying attribute
    
      void main() {
        gl_FragColor = texture2D(texture, vUv);
      }
    </script>

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-10-28
      • 2015-04-12
      • 2013-05-01
      • 1970-01-01
      • 2018-10-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多