0

I'm trying to create a shader for a 3d object generated from Maya.

I can get the 3d object to load, however when I try to apply the shader I'm getting this error:

GL ERROR :GL_INVALID_OPERATION : glDrawElements: attempt to access out of range vertices in attribute 1

From what I have read, I understand that I need to define the vertices, but I have no idea on how to do this.

Here is the code:

<html xmlns="http://www.w3.org/1999/xhtml"><head>
<title>3D render</title>
<script src="js/three.min.js"></script>
<script src="js/OBJLoader.js"></script>
</head>

<body onload="start()" >
<script> 
    var gl;
    var camera, cubeCamera, scene, renderer; 
    var intervalProgress;  

    var fov = 70,

        lon = 0, 
        lat = 70,  
        phi = 0, 
        theta = 0;

    init(); 
    animate(); 

    function init() {

        camera = new THREE.PerspectiveCamera( fov, window.innerWidth / window.innerHeight, 1, 1000 ); 

        scene = new THREE.Scene();

        var mesh = new THREE.Mesh( new THREE.SphereGeometry( 500, 60, 40 ), new THREE.MeshBasicMaterial({color: 0xFFFFFF}) ); 
        mesh.scale.x = -1;

        scene.add( mesh );


        var light = new THREE.PointLight(0xFFFFFF);
        light.position.set(0, 0, 500);
        scene.add(light);

        renderer = new THREE.WebGLRenderer( { antialias: true } ); 
        renderer.setSize( window.innerWidth, window.innerHeight ); 

        document.body.appendChild( renderer.domElement );


            var material = new THREE.MeshBasicMaterial( { color:0xFFFFFF } );

        var loader = new THREE.OBJLoader();

        loader.load( "mesh/3d.obj", function ( object ) {

                object.traverse(function(child) {
                        if (child instanceof THREE.Mesh) {


                            var vertShader = document.getElementById('vertexShader').innerHTML;
                            var fragShader = document.getElementById('fragmentShader').innerHTML;                           


                            var uniforms = {
                                texture1: { type: "t", value: THREE.ImageUtils.loadTexture( "textures/texture.jpg" ) }
                            };

                            material = new THREE.ShaderMaterial({
                                uniforms: uniforms,
                                vertexShader: vertShader,
                                fragmentShader: fragShader,
                                wrapping: THREE.ClampToEdgeWrapping,
                                shading: THREE.SmoothShading,
                                side: THREE.DoubleSide
                            });


                            child.material = material;
                            child.material.needsUpdate = true;
                            // child.material.color.setRGB (1, 0, 0);


                        }
                    });

                    //SET SCALE, POSITION AND ROTATION OF OBJECT  
                    object.scale.set( 40,40,40 );
                    object.rotation.x = 0;
                    object.rotation.y = 90; 
                    object.position.y = 0;

                    scene.add(object);

              }); 



    }

    function animate() { 
        requestAnimationFrame( animate );
        render(); 
    }

    function render() {

        var time = Date.now(); 
        lon += .15; 
        lat = Math.max( - 85, Math.min( 85, lat ) );
        phi = THREE.Math.degToRad( 90 - lat );
        theta = THREE.Math.degToRad( lon );

        camera.position.x = 100 * Math.sin( phi ) * Math.cos( theta );
        camera.position.y = 100 * Math.cos( phi );
        camera.position.z = 100 * Math.sin( phi ) * Math.sin( theta ); 
        camera.lookAt( scene.position ); 

        renderer.render( scene, camera ); 
    }

    function start() {
      var canvas = document.getElementById("glcanvas");

      gl = initWebGL(canvas);      // Initialize the GL context

      if (gl) {
        gl.clearColor(0.0, 0.0, 0.0, 1.0);                      // Set clear color to black, fully opaque
        gl.enable(gl.DEPTH_TEST);                               // Enable depth testing
        gl.depthFunc(gl.LEQUAL);                                // Near things obscure far things
        gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT);      // Clear the color as well as the depth buffer.
      }
    }

    function initWebGL(canvas) {
      gl = null;

      try {
        // Try to grab the standard context. If it fails, fallback to experimental.
        gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
      }
      catch(e) {}

      // If we don't have a GL context, give up now
      if (!gl) {
        alert("Unable to initialize WebGL. Your browser may not support it.");
        gl = null;
      }

      return gl;
    }

</script> 

<script id="vertexShader" type="x-shader/x-vertex">
varying vec2 vUv;

void main() {
    vUv = uv;

    gl_Position =   projectionMatrix * 
                    modelViewMatrix * 
                    vec4(position,1.0);
}
</script>

<script id="fragmentShader" type="x-shader/x-fragment">
uniform sampler2D texture1;

varying vec2 vUv;

void main() {
    gl_FragColor = vec4(0.5, 0.2, 1.0, 1.0); 
}
</script>
<canvas id="glcanvas" width="1" height="1"></canvas>
<canvas width="1920" height="951"></canvas>
</body>
</html>
Ajq
  • 384
  • 2
  • 4
  • 15
  • you do not have `scene.add( object )`. is it somewhere else in your code? – gaitat Oct 31 '15 at 02:35
  • Yes, I did have it in there, I just forgot to copy it into this question. I have edited the question to include the complete code. If I comment out the shader it works. – Ajq Oct 31 '15 at 03:36
  • 1
    you should move the `shader` and `material` code outside the `object.traverse()` code. Why are you keep downloading the texture for each child? It is always the same. – gaitat Oct 31 '15 at 03:41
  • Yes, you're right. But I will still have the same problem. – Ajq Oct 31 '15 at 03:54
  • do `var texture = THREE.ImageUtils.loadTexture( "textures/texture.jpg" )` and then `value: texture`. actually what you should do is use the texture loader manager and assign the texture to the material only when the texture has downloaded. look at http://threejs.org/docs/#Reference/Loaders/TextureLoader – gaitat Oct 31 '15 at 03:59
  • It's trying to write the data outside the buffer, so I don't think it has anything to do with the texture. If I remove the texture completely, I still get the same error. I think what needs to happen is that I have to somehow load in the 3d model into a buffer array and then pass that to the shader. – Ajq Oct 31 '15 at 04:23
  • Cabbibo's answer on this post did the trick. http://stackoverflow.com/questions/20774648/three-js-generate-uv-coordinate – Ajq Oct 31 '15 at 12:12
  • Do listen gaitat's advice, there seems to be no reason to create a new material in your traverse function. I would expect you will find quite a performance increase if you just create the material once and reference it in your traverse function. – 2pha Nov 01 '15 at 08:28
  • Yes, I did listen.. As it turns out, all I needed was this line: child.geometry = new THREE.Geometry().fromBufferGeometry( child.geometry ); – Ajq Nov 01 '15 at 09:56

1 Answers1

1

I found the problem, it was a combination of all mentioned above, so I cleaned up the code: here is the working example:

function loadObj(name, metal){

var material = null;
loadTexture('textures/'+metal+'.jpg', function(texture){
    loadTexture('normal/243-normal.jpg', function(texture2){
        var material = createMetalShader(metal, texture, texture2);
        var loader = new THREE.OBJLoader();

        loader.load( "mesh/"+name+".obj", function ( object ) {


            object.traverse(function(child) {
                if (child instanceof THREE.Mesh) {

                    child.geometry = new THREE.Geometry().fromBufferGeometry( child.geometry );  
                    child.geometry.mergeVertices(); 

                    //assignUVs(child.geometry);

                    child.material = material;
                    child.verticesNeedUpdate = true;
                    child.normalsNeedUpdate = true;
                    child.uvsNeedUpdate = true;

                    child.material.shading = THREE.SmoothShading;
                    child.geometry.computeVertexNormals(); 

                }

                object.scale.set( 30,30,30 );
                object.rotation.x = 0;
                object.rotation.y = 90; 
                object.position.y = 0;
                scene.add(object);
                currentMetalObject = object;
            });
        });
    });
});


}

function loadTexture(path, callback){
var loader = new THREE.TextureLoader();

// load a resource
loader.load(path,function ( texture ) {
        callback(texture);
    },
    function ( xhr ) {
        console.log( (xhr.loaded / xhr.total * 100) + '% loaded' );
    },
    function ( xhr ) {
        console.log( 'An error happened' );
    }
);


}

function createMetalShader(metal, textureMatCap, textureNormal){
var material = new THREE.ShaderMaterial({
    uniforms: {
        tNormal: {
            type: 't',
            value: textureNormal
        },
        tMatCap: {
            type: 't',
            value: textureMatCap
        },
        time: {
            type: 'f',
            value: 0
        },
        bump: {
            type: 'f',
            value: 0
        },
        noise: {
            type: 'f',
            value: .04
        },
        repeat: {
            type: 'v2',
            value: new THREE.Vector2(1, 1)
        },
        useNormal: {
            type: 'f',
            value: 0
        },
        useRim: {
            type: 'f',
            value: 0
        },
        rimPower: {
            type: 'f',
            value: 2
        },
        useScreen: {
            type: 'f',
            value: 0
        },
        normalScale: {
            type: 'f',
            value: .5
        },
        normalRepeat: {
            type: 'f',
            value: 1
        }
    },
    vertexShader: document.getElementById('vertexShader').textContent,
    fragmentShader: document.getElementById('fragmentShader').textContent,
    wrapping: THREE.ClampToEdgeWrapping,
    shading: THREE.SmoothShading,
    side: THREE.DoubleSide
});
material.uniforms.tMatCap.value.wrapS = material.uniforms.tMatCap.value.wrapT = THREE.ClampToEdgeWrapping;
material.uniforms.tNormal.value.wrapS = material.uniforms.tNormal.value.wrapT = THREE.RepeatWrapping;
material.name = name;
return material;
}
Ajq
  • 384
  • 2
  • 4
  • 15