I want to load shader from external file. This shader is correct, as it works when inserted in <script>
tag. I do the following:
var loader = new THREE.FileLoader(); loader.load('shader.vert',function ( data ) {vShader = data;},); loader.load('shader.frag',function ( data ) {fShader = data;},);
Later I use it as:
var material = new THREE.ShaderMaterial({ uniforms: uniforms, vertexShader: vShader, fragmentShader: fShader, });
The problem is that when i reload the page I mave errors, that shaders are not defined. When I type vShader in console it prints me out what I want. When I start to debug (breakpoint inside init() function vShader is undefined.
Could You explain my mistake?
The whole code:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <script src="https://threejs.org/build/three.min.js"></script> </head> <body> <div class="WebGL-output" id="WebGL-output"></div> <script id="vertexShader" type="x-shader/x-vertex" src="shader.vert"> 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 vec3 color; varying vec2 vUv; void main() { gl_FragColor = vec4(color,1.); } </script> <script> var renderer,uniforms,vShader,fShader,camera,scene; var loader = new THREE.FileLoader(); init(); animate(); function init(){ renderer = new THREE.WebGLRenderer renderer.setSize(window.innerWidth, window.innerHeight); document.getElementById('WebGL-output').appendChild(renderer.domElement); camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 10000); camera.position.z = 5000; scene = new THREE.Scene(); //******* DO NOT WORK loader.load('plaid.frag',function ( data ) {fShader = data;},); loader.load('plaid.vert',function ( data ) {vShader = data;},); // ****** WORK //fShader = document.getElementById('fragmentShader').text; //vShader = document.getElementById('vertexShader').text; // ************************** uniforms = { "color" : { type : "c", //value :new THREE.Color(0xf0f0f0) value :new THREE.Color(0x00ff00) }, }; var material = new THREE.ShaderMaterial({ uniforms: uniforms, vertexShader: vShader, fragmentShader: fShader, }); // Create circles and add to scene. var geometry = new THREE.CircleGeometry(100, 50); mesh = new THREE.Mesh(geometry, material); scene.add(mesh); var light = new THREE.AmbientLight(0x000000); // soft white light scene.add(light); } function animate() { requestAnimationFrame(animate); renderer.render(scene, camera); } </script> </body> </html>
Advertisement
Answer
Your mistake is that loading files in browsers is asychronous. So first you have this code
loader.load('plaid.frag',function ( data ) {fShader = data;},); loader.load('plaid.vert',function ( data ) {vShader = data;},);
Those functions you made that set fShader
and vShader
will not be called until some time later (how ever long it takes to download the files)
But, just a few lines down you use both fShader
and vShader
immediately even though they haven’t downloaded yet
var material = new THREE.ShaderMaterial({ uniforms: uniforms, vertexShader: vShader, fragmentShader: fShader, });
If you really want to load from external files you need to structure your code to wait for them to download.
As one example
var renderer,uniforms,vShader,fShader,camera,scene; var loader = new THREE.FileLoader(); init(); function init() { renderer = new THREE.WebGLRenderer renderer.setSize(window.innerWidth, window.innerHeight); document.getElementById('WebGL-output').appendChild(renderer.domElement); camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 10000); camera.position.z = 5000; scene = new THREE.Scene(); var numFilesLeft = 2; function runMoreIfDone() { --numFilesLeft; if (numFilesLeft === 0) { more(); } } //******* DO NOT WORK loader.load('plaid.frag',function ( data ) {fShader = data; runMoreIfDone(); },); loader.load('plaid.vert',function ( data ) {vShader = data; runMoreIfDone(); },); // ****** WORK //fShader = document.getElementById('fragmentShader').text; //vShader = document.getElementById('vertexShader').text; } function more() { // ************************** uniforms = { "color" : { type : "c", //value :new THREE.Color(0xf0f0f0) value :new THREE.Color(0x00ff00) }, }; var material = new THREE.ShaderMaterial({ uniforms: uniforms, vertexShader: vShader, fragmentShader: fShader, }); // Create circles and add to scene. var geometry = new THREE.CircleGeometry(100, 50); mesh = new THREE.Mesh(geometry, material); scene.add(mesh); var light = new THREE.AmbientLight(0x000000); // soft white light scene.add(light); animate(); } function animate() { requestAnimationFrame(animate); renderer.render(scene, camera); }