Shaders & structs

早过忘川 提交于 2019-12-13 08:46:10

问题


Edit 16 August, 2016

I finally managed to get it to throw an error that might expose what's going on. In my latest code edit, I was adding the ability to hot-swap between fragment shaders. This works fine for shaders with singleton uniforms, but swapping to a shader with a structure throws the following error:

Uncaught TypeError: Cannot read property 'x' of undefined

Call stack:

setValue3fv @ three.js:31915
THREE.WebGLUniforms.StructuredUniform.setValue @ three.js:32148
THREE.WebGLUniforms.WebGLUniforms.upload @ three.js:32285
setProgram @ three.js:26733 renderBufferDirect @ three.js:25492
renderObjects @ three.js:26303
render @ three.js:26044
draw @ VM160:191doStructs @ VM86:148

It looks like it's building the structure uniforms incorrectly (it's trying to reference property value for a THREE.Color which only has r, g, and ,b). I'm continuing to dig, but maybe this new info will trigger a lightbulb for someone.

Original Post & Code

According to a conversation here, Three.js has the ability to use structs as shader uniforms. I'm now trying to use a shader that has structs, simplified to the bare minimum, and still can't get it to recognize them as structs.

In the fiddle below, just change the WITH_STRUCTS value to true/false and re-run to see the difference.

I've also tried defining the uniforms like this, but it didn't make a difference.

uniforms: {
    "colorValues": {
        "color": { type: "c", value: new THREE.Color(0x00ff00) }
    },
    "opacityValues": {
        "opacity": { type: "f", value: 1.0 }
    }
}

Did I miss some properties, or am I approaching this entirely incorrectly?

jsFiddle: http://jsfiddle.net/TheJim01/546syLur/

HTML:

<script src="http://threejs.org/build/three.js"></script>
<script src="http://threejs.org/examples/js/controls/TrackballControls.js"></script>
<script src="http://threejs.org/examples/js/libs/stats.min.js"></script>
<div id="host"></div>

<script>
// INITIALIZE
var WIDTH = window.innerWidth,
    HEIGHT = window.innerHeight,
    FOV = 35,
    NEAR = 1,
    FAR = 1000;

var renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(WIDTH, HEIGHT);
document.getElementById('host').appendChild(renderer.domElement);

var camera = new THREE.PerspectiveCamera(FOV, WIDTH / HEIGHT, NEAR, FAR);
camera.position.z = 250;

var trackballControl = new THREE.TrackballControls(camera, renderer.domElement);
trackballControl.rotateSpeed = 5.0; // need to speed it up a little

var scene = new THREE.Scene();

var light = new THREE.PointLight(0xffffff, 1, Infinity);
light.position.copy(camera.position);

scene.add(light);

function draw(){
    light.position.copy(camera.position);
    renderer.render(scene, camera);
}
trackballControl.addEventListener('change', draw);

function navStartHandler(e) {
  renderer.domElement.addEventListener('mousemove', navMoveHandler);
  renderer.domElement.addEventListener('mouseup', navEndHandler);
}
function navMoveHandler(e) {
  trackballControl.update();
}
function navEndHandler(e) {
  renderer.domElement.removeEventListener('mousemove', navMoveHandler);
  renderer.domElement.removeEventListener('mouseup', navEndHandler);
}

renderer.domElement.addEventListener('mousedown', navStartHandler);
renderer.domElement.addEventListener('mousewheel', navMoveHandler);
</script>

<fieldset id="controls">
  <legend>Controls</legend>
  <input id="drawButton" type="button" value="DRAW" />
  <input id="useStructsCheck" type="checkbox" /><label for="useStructsCheck">Use Structs</label>
</fieldset>

CSS:

html, body{
    padding: 0;
    margin: 0;
    width: 100%;
    overflow: hidden;
}

#controls {
    position: absolute;
    top: 20px;
    left: 20px;
    z-index: 99;
    color: white;
    border-radius: 10px;
}

#host {
    width: 100%;
    height: 100%;
}

JavaScript:

// NOTE: To run this demo, you MUST use the HTTP protocol, not HTTPS!

var WITH_STRUCTS = false;

var vShaderCode = [
    "precision highp float;",
    "precision highp int;",

    "uniform mat4 modelViewMatrix;",
    "uniform mat4 projectionMatrix;",

    "attribute vec3 position;",

    "void main() {",
        "vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
        "gl_Position = projectionMatrix * mvPosition;",
    "}"
].join("\n");

var fShaderCode_structs = [
    "precision highp float;",
    "precision highp int;",

    "struct ColorStruct{",
        "vec3 value;",
    "};",
    "struct OpacityStruct{",
        "float value;",
    "};",
    "uniform ColorStruct color;",
    "uniform OpacityStruct opacity;",

    "void main() {",
        "gl_FragColor = vec4(color.value, opacity.value);",
    "}"
].join("\n");

var fShaderCode_nostructs = [
    "precision highp float;",
    "precision highp int;",

    "uniform vec3 color;",
    "uniform float opacity;",

    "void main() {",
        "gl_FragColor = vec4(color, opacity);",
    "}"
].join("\n");

var geo = new THREE.BoxBufferGeometry(30, 30, 30);

var mat = new THREE.RawShaderMaterial({
        uniforms: {
        "color": { type: "c", value: new THREE.Color(0xff0000) },
            "opacity": { type: "1f", value: 1.0 },
            "color.value": { type: "c", value: new THREE.Color(0x00ff00) },
            "opacity.value": { type: "1f", value: 1.0 }
        },
        vertexShader: vShaderCode,
        fragmentShader: (WITH_STRUCTS) ? fShaderCode_structs : fShaderCode_nostructs
    });

var msh = new THREE.Mesh(geo, mat);
scene.add(msh);

draw();

// Draw button
// Making this an external function rather than referencing draw directly, in case we want to update draw with parameters.
function doDraw() {
  draw();
}
document.getElementById("drawButton").addEventListener("click", doDraw);

// Use Structs checkbox
function doStructs() {
  WITH_STRUCTS = !WITH_STRUCTS;
  if (WITH_STRUCTS) {
    mat.fragmentShader = fShaderCode_structs;
  }
  else {
    mat.fragmentShader = fShaderCode_nostructs;
  }
  mat.needsUpdate = true;
  draw();
}
document.getElementById("useStructsCheck").addEventListener("click", doStructs);

回答1:


This is a lesson in how a couple small and stupid mistakes can make the whole thing crash and burn.

I had named my singleton uniforms the same thing as my structured uniforms. When the uniform parser was trying to assign values, it tried to assign the singleton value onto the structure object. That didn't work, naturally, and the whole thing fell apart.

It also seems that the "alternate" uniform definition was the way to go. I'm not sure why it din't work originally, but I have a feeling it was due to a naming mismatch, like above.

Here's the working code chunks, and I've update the fiddle linked in my original post.

New Fragment Shader (only the one WITH structs)

var fShaderCode_structs = [
    "precision highp float;",
    "precision highp int;",

    "struct ColorStruct{",
        "vec3 cvalue;",
    "};",
    "struct OpacityStruct{",
        "float ovalue;",
    "};",
    "uniform ColorStruct colorS;",
    "uniform OpacityStruct opacityS;",

    "void main() {",
        "gl_FragColor = vec4(colorS.cvalue, opacityS.ovalue);",
    "}"
].join("\n");

New Material Definition

var mat = new THREE.RawShaderMaterial({
    uniforms: {
        "color": { type: "c", value: new THREE.Color(0xff0000) },
            "opacity": { type: "1f", value: 1.0 },
            "colorS": {
                value: { type: "c", cvalue: new THREE.Color(0x00ff00) },
            },
            "opacityS": {
                value: { type: "1f", ovalue: 1.0 }
            }
        },
        vertexShader: vShaderCode,
        fragmentShader: (WITH_STRUCTS) ? fShaderCode_structs : fShaderCode_nostructs
    });


来源:https://stackoverflow.com/questions/38833195/shaders-structs

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!