如何在three.js中创建纹理,然后通过着色器渲染?

时间:2019-01-24 06:22:01

标签: javascript three.js shader

现在我有了数组:Uint8ClampedArray(size:512*512*4),我想将此数组转换为three.js中的纹理,然后使用Three.ShaderMaterial渲染对象。

new THREE.ShaderMaterial( {
    vertexShader: document.querySelector( '#blendModel-vert').textContent.trim(),
    fragmentShader: document.querySelector( '#blendModel-frag' ).textContent.trim(),
    uniforms: {
        buffer:  { value: texture },
    }
});

着色器如下:

<script id="blendModel-vert" type="x-shader/x-vertex">
    varying vec2 vUv;
    varying vec4 gPosition;
    void main() {
        vUv = uv;
        gPosition=gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    }
</script>
<script id="blendModel-frag" type="x-shader/x-fragment">
    varying vec2 vUv;
    uniform sampler2D buffer;
    varying vec4 gPosition;
    void main() {
        gl_FragColor.rgb = texture2D(buffer, vUv).rgb;
        gl_FragColor.a = 1.0;
    }
</script>

1 个答案:

答案 0 :(得分:0)

一种可能性是使用https://reactjs.org/docs/state-and-lifecycle.html
提供给Three.DataTexture的数据缓冲区必须是 Uint8Array 类型,而不是Uint8ClampedArray。文档中已提及。
缓冲区必须由[0,255]范围内的字节值组成:

例如

let t_cx = 512;
let t_cy = 512;
let t_data = new Uint8Array(4 * t_cx * t_cy);

for ( let i = 0; i < t_cx; i ++ ) {
    for ( let j = 0; j < t_cy; j ++ ) {

        let id = j*t_cx*4 + i*4;

        let r = i / (t_cx-1);
        let g = j / (t_cx-1);
        let b = (1-r)*(1-g);

        t_data[id + 0] = r * 255;
        t_data[id + 1] = g * 255;
        t_data[id + 2] = b * 255;
        t_data[id + 3] = 255;
    }
}

此外,必须在新创建的纹理对象上将属性Three.DataTexture设置为true

var texture = new THREE.DataTexture( t_data, t_cx, t_cy, THREE.RGBAFormat );
texture.needsUpdate = true;

还可以创建一个临时2D画布和图像对象。可以像往常一样由THREE.TextureLoader来配合:

let canvas = document.createElement( 'canvas' );
let ctx = canvas.getContext('2d');
canvas.width = t_cx;
canvas.height = t_cy;
let idata = ctx.createImageData( t_cx, t_cy );
idata.data.set( t_data );
ctx.putImageData( idata, 0, 0 );
let dataUri = canvas.toDataURL();

var texture = new THREE.TextureLoader().load( dataUri );

canvas.remove();

请参见示例:

(function onLoad() {
  var loader, camera, scene, renderer, orbitControls;
  
  init();
  animate();

  function init() {
    renderer = new THREE.WebGLRenderer({
      antialias: true,
      alpha: true
    });
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(window.innerWidth, window.innerHeight);
    renderer.shadowMap.enabled = true;
    document.body.appendChild(renderer.domElement);

    camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 100);
    camera.position.set(0, 1, -2);
    //camera.lookAt( -1, 0, 0 );

    loader = new THREE.TextureLoader();
    loader.setCrossOrigin("");

    scene = new THREE.Scene();
    scene.background = new THREE.Color(0xffffff);
    scene.add(camera);
    window.onresize = resize;
    
    var ambientLight = new THREE.AmbientLight(0x404040);
    scene.add(ambientLight);

    var directionalLight = new THREE.DirectionalLight( 0xffffff, 0.5 );
    directionalLight.position.set(1,2,1.5);
    scene.add( directionalLight );

    orbitControls = new THREE.OrbitControls(camera);
    
    addGridHelper();
    createModel();

  }

  function createModel() {

    let t_cx = 512;
    let t_cy = 512;
    let t_data = new Uint8Array(4 * t_cx * t_cy);

    for ( let i = 0; i < t_cx; i ++ ) {
        for ( let j = 0; j < t_cy; j ++ ) {
            
            let id = j*t_cx*4 + i*4;

            let r = i / (t_cx-1);
            let g = j / (t_cx-1);
            let b = (1-r)*(1-g);

            t_data[id + 0] = r * 255;
            t_data[id + 1] = g * 255;
            t_data[id + 2] = b * 255;
            t_data[id + 3] = 255;
        }
    }

    var testMode = 1
    var texture;

    if ( testMode == 1 ) {
        let canvas = document.createElement( 'canvas' );
        let ctx = canvas.getContext('2d');
        canvas.width = t_cx;
        canvas.height = t_cy;
        let idata = ctx.createImageData( t_cx, t_cy );
        idata.data.set( t_data );
        ctx.putImageData( idata, 0, 0 );
        let dataUri = canvas.toDataURL();
        
        texture = new THREE.TextureLoader().load( dataUri );

        canvas.remove();
    } else {
        texture = new THREE.DataTexture( t_data, t_cx, t_cy, THREE.RGBAFormat );
        texture.needsUpdate = true;
    }
    

    var material = new THREE.ShaderMaterial({  
          vertexShader: document.getElementById('vertex-shader').textContent,
          fragmentShader: document.getElementById('fragment-shader').textContent,
          uniforms : {
              buffer: {type: 't', value: texture}
          }
    });

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

    scene.add(mesh);
  }

  function addGridHelper() {
    
    var helper = new THREE.GridHelper(100, 100);
    helper.material.opacity = 0.25;
    helper.material.transparent = true;
    scene.add(helper);

    var axis = new THREE.AxesHelper(1000);
    scene.add(axis);
  }

  function resize() {
    
    var aspect = window.innerWidth / window.innerHeight;
    renderer.setSize(window.innerWidth, window.innerHeight);
    camera.aspect = aspect;
    camera.updateProjectionMatrix();
  }

  function animate() {
    requestAnimationFrame(animate);
    orbitControls.update();
    render();
  }

  function render() {
    renderer.render(scene, camera);
  }
})();
<script type='x-shader/x-vertex' id='vertex-shader'>
varying vec2 vUv;
void main() {
    vUv = uv;
    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
</script>
<script type='x-shader/x-fragment' id='fragment-shader'>
precision highp float;
uniform sampler2D buffer;
varying vec2 vUv;
void main(){
    gl_FragColor = texture2D(buffer, vUv);
}
</script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/100/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>