Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom render passess & Implementing shadows #186

Open
Arsakes opened this issue Mar 1, 2016 · 3 comments
Open

Custom render passess & Implementing shadows #186

Arsakes opened this issue Mar 1, 2016 · 3 comments

Comments

@Arsakes
Copy link

Arsakes commented Mar 1, 2016

What is the simple way to implement shadows given tools we have withing XML3D?

What I need is two pasess.
One that renders whole scene geometry into depth map, then sends this as a texture to the next pass.
Second pass also renders whole geometry but with different shaders. Is that possible?

I mean somehow easier than manually giving whole geometry to the pass and setting shaders.
The whole difference would be replacement shaders within "normal" pass.

EDIT: if it would be too hard to do shadows I can somehow bake them.
How can I save current canvas into downloadable image?

EDIT2:
There is an extension to webgl that allows you to render depth into the same buffer.

@csvurt
Copy link
Contributor

csvurt commented Mar 2, 2016

XML3D already has basic shadow support for spotlights, you can turn it on by giving the light a castShadow boolean:

<light model="urn:xml3d:light:spot">
     <bool name="castShadow">true</bool>
     <float name="shadowBias">0.001</float>
 </light>

If you need soft shadows or ones that work for different light sources too then you'll need to write the necessary render passes for that. Since you have access to the WebGL context (gl) you can do pretty much anything there, including activating extensions like WEBGL_DEPTH_TEXTURE.

Just be wary of any shadow rendering algorithm that requires rendering to floating point textures. The extensions governing that are a mess and the majority of devices out there probably don't support it.

Every RenderPass gets the scene object as an argument to its render function, if you want to render everything with a different shader you only need to bind that shader and iterate over the objects in the scene. Check out pick-object.js for example, it renders all objects in the scene with a special picking shader. You can ignore the viewMatrix and projMatrix arguments to the render function, those are for picking only. Also you only need to sort the objects once in your very first RenderPass, unless you change your camera in between (ie. when rendering from the point of view of a light).

You can save the contents of the canvas by generating a dataURL for it and either downloading it or using it as the src for an img element:

var url = document.querySelector("canvas").toDataURL();
var img = document.createElement("img");
img.src = url;
document.appendChild(img);

@Arsakes
Copy link
Author

Arsakes commented Mar 5, 2016

What do you mean I can ignore viewMatrix and projMatrix - are those uniforms provided automatically?

Appending new textures to default pass
How can I append one additonal uniform to mainPass programs? (It would be depth texture of the whole scene from camera perspective).
I'm talking about this function
createSceneRenderPass(target) that creates a copy of the same scene render pass that XML3D uses internally to draw the scene.
The thing I would like to do is to attach one additional texture (depth map from previous pass).
Would it be possible/?

Objects Sorting
Do I need to sort objects manually - as it is done in pick-objects? Depth test wouldn't be enough for the case transparency can be ignored?
Let me explain what I mean.

for (var j = 0, n = sortedObjects.zLayers.length; j < n; j++) {
            var zLayer = sortedObjects.zLayers[j];
            gl.clear(gl.DEPTH_BUFFER_BIT);
            if (sortedObjects.opaque[zLayer]) {
                for (var prg in sortedObjects.opaque[zLayer]) {
                    this.renderObjects(sortedObjects.opaque[zLayer][prg], program, viewMatrix, projMatrix);
                }
            }
            if (sortedObjects.transparent[zLayer]) {
                this.renderObjects(sortedObjects.transparent[zLayer], program, viewMatrix, projMatrix);
            }
        }

Couldn't this be changed for:
this.renderObjects(objects, program, viewMatrix, projMatrix);
Just for rendering depth map (no transparency or so).

Whereas renderObjects has following form (compare with the one from pickPass):

renderObjects: (function () {
        var c_mvp = mat4.create(), c_uniformCollection = {
            envBase: {},
            envOverride: null,
            sysBase: {}
        }, c_systemUniformNames = [ "modelViewProjectionMatrix"];

        return function (objects, program, viewMatrix, projMatrix) {
            for (var i=0; i < objects.length; i++) {
                var obj = objects[i];
                var mesh = obj.mesh;

                if (!obj.visible)
                    continue;

                if (viewMatrix && projMatrix) {
                    obj.updateModelViewMatrix(viewMatrix);
                    obj.updateModelViewProjectionMatrix(projMatrix);
                }
                obj.getModelViewProjectionMatrix(c_mvp);   
                c_uniformCollection.sysBase["modelViewProjectionMatrix"] = c_mvp;

                program.setUniformVariables(null, c_systemUniformNames, c_uniformCollection);
                mesh.draw(program);
            }
        };
    }()),

@csvurt
Copy link
Contributor

csvurt commented Mar 8, 2016

The render pass has access to the active camera through scene.getActiveView() and you can pull the view and projection matrices out of there. This is done in the ForwardRenderPass (forward.js). The ones passed directly to the render function are only used for picking passes triggered by getElementByRay, since they use a temporary camera created from the origin and direction of the ray. That's why you can ignore them, but you still need to set the uniform variables with the activeView's matrices.

Check out vertexattribute-pass.js, that's really the most bare bones way of rendering all the objects in the scene with a different shader program. It uses the SceneRenderPass as a "parent class", uses a custom shader and then calls the SceneRenderPass's renderObjectsToActiveBuffer function to render all the scene objects with that custom shader. If you need to set a uniform (like a depth texture) you would add that to the scene.systemUniforms object before calling renderObjectsToActiveBuffer like this:

scene.systemUniforms["depthTexture"] = [this.inputs.depthBuffer.colorTarget.handle];

this.renderObjectsToActiveBuffer(scene.ready, scene, target, scene.systemUniforms, [], {
            transparent: false,
            program: this._program
        });

The only catch here is that SceneRenderPass is not exposed outside of xml3d.js, so if you want to create your own render pass using it as a parent class you'll have to add it to the list of things that get exposed in src/global.js and build your own copy of xml3d.js:

XML3D.webgl = {};
XML3D.webgl.BaseRenderTree = require("./renderer/webgl/render-trees/base.js");
XML3D.webgl.BaseRenderPass = require("./renderer/webgl/render-passes/base.js");
XML3D.webgl.SceneRenderPass = require("./renderer/webgl/render-passes/scene-pass.js");

If you don't need sorting (for example for transparency) then you don't need to sort the objects, especially if you're rendering them all with a custom shader anyway. Just note that if you use the z-index property on any of your objects then you will have to sort them, since that has an impact on the depth buffer too. If you don't need support for z-index or transparency then you can just render all the objects in any order.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants