0
votes

I'm learning about shaders using a number of existing webgl tutorials, and I was hoping there would be a way to attach a compiled shader program to a threejs shadermaterial, but I'm getting stuck. If possible it would be very nice to set attributes and uniforms using the gl methods, and then set the shader program on the material. Here's what I've tried.

<!doctype html>
<html>
    <head>

        <script src="http://threejs.org/build/three.min.js"></script>
        <meta charset="utf-8" />
        <title>Sample Three.js</title>
        <style>
            #container {
                background: #000;
                width: 400px;
                height: 300px;
            }
        </style>
    </head>
    <body>


    </body>

    <script type="x-shader/x-vertex" id="vertexshader">
        // switch on high precision floats
        #ifdef GL_ES
        precision highp float;
        #endif
        uniform mat4 projectionmyMatrix;
        attribute vec3 vertexPos;
        attribute float displacement;
        uniform float amplitude;

        void main()
        {
            vec3 newPos = vertexPos;
            gl_Position = projectionMatrix * modelViewMatrix * vec4(newPos,1.0);
        }

    </script>

    <script type="x-shader/x-fragment" id="fragmentshader">
        #ifdef GL_ES
        precision highp float;
        #endif

        void main( void ) {

           gl_FragColor = vec4( 1.0,1.0,1.0,1.0);

        }
    </script>

    <!-- End Shaders -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js"></script>

    <script type="text/javascript">
    // set the scene size
    var WIDTH = 800,
        HEIGHT = 600;

    // set some camera attributes
    var VIEW_ANGLE = 45,
        ASPECT = WIDTH / HEIGHT,
        NEAR = 1,
        FAR = 1000;

    // get the DOM element to attach to
    // - assume we've got jQuery to hand
    var $container = $('body');

    // create a WebGL renderer, camera
    // and a scene
    var renderer = new THREE.WebGLRenderer();
    var camera = new THREE.PerspectiveCamera(
        VIEW_ANGLE,
    ASPECT,
    NEAR,
    FAR  );
    var scene = new THREE.Scene();

    // the camera starts at 0,0,0 so pull it back
    camera.position.z = 300;

    // start the renderer
    renderer.setSize(WIDTH, HEIGHT);

    // attach the render-supplied DOM element
    $container.append(renderer.domElement);

    // set up the sphere vars
    var radius = 50, segments = 16, rings = 16;


    // create the sphere's material
    var shaderMaterial = new THREE.ShaderMaterial({
        vertexShader:   $('#vertexshader').text(),
        fragmentShader: $('#fragmentshader').text()
    });
    // create a new mesh with sphere geometry -
    // we will cover the sphereMaterial next!
    var sphere = new THREE.Mesh(
       new THREE.SphereGeometry(radius, segments, rings),
       shaderMaterial);

    //filling the attribute vertex array

    // add the sphere and camera to the scene
    scene.add(sphere);
    scene.add(camera);
    renderer.compile(scene,camera)

    var gl = renderer.getContext()
    var sq = createSquare(gl)
    var prg = shaderMaterial.program.program
    var posAttr = gl.getAttribLocation(prg,'vertexPos')


    // set the vertex buffer to be drawn
    gl.bindBuffer(gl.ARRAY_BUFFER, sq.buffer);

    // set the shader to use
    gl.useProgram(prg);

    // connect up the shader parameters: vertex position and projection/model matrices
    gl.vertexAttribPointer(posAttr, sq.vertSize, gl.FLOAT, false, 0, 0);

    renderer.compile(scene,camera)
    // create a rendering loop
    var frame = 0;
    function update() {
        frame += .01
        renderer.render(scene, camera);
        requestAnimationFrame(update)
    }
    requestAnimationFrame(update)

    </script>
</html>

I would prefer not to have to translate from the tutorials into the uniforms, attributes syntax used by three.js denoted below

```

   var attributes = {
  displacement: {
    type: 'f', // a float
    value: [] // an empty array
  }
};
var uniforms = {
  amplitude: {
    type: 'f', // a float
    value: 1
  }
};

var vShader = $('#vertexshader');
var fShader = $('#fragmentshader');

// create the final material
var shaderMaterial =
    new THREE.MeshShaderMaterial({
      uniforms:       uniforms,
      attributes:     attributes,
      vertexShader:   vShader.text(),
      fragmentShader: fShader.text()
    });
...
1

1 Answers

0
votes

No, this approach is neither recommended nor supported. Instead of using the raw WebGL context, you have two options:

  • You can use THREE.ShaderMaterial for a custom shader definition. three.js automatically provides some built-in attributes and uniforms (e.g modelViewMatrix or projectionMatrix) which are frequently used by vertex and fragment shaders. The official doc page provides a lot of information.
  • THREE.RawShaderMaterial is a more lightweight option since three.js does not provide the mentioned built-in uniforms and attributes.

The following two basic examples show the usage of both materials with the latest version of three.js (R91):

https://threejs.org/examples/#webgl_shader https://threejs.org/examples/#webgl_buffergeometry_rawshader

I recommend to work with these examples and not with potentially outdated tutorials. For example attributes are no parameter of ShaderMaterial anymore. Instead, attribute data are part of the geometry.