how to bind a player position to a shader with melonjs?

164 Views Asked by At

I've created a glsl shader as:

<script id="player-fragment-shader" type="x-shader/x-fragment">
precision highp float;

varying vec3 fNormal;
uniform vec2 resolution;

float circle(in vec2 _pos, in float _radius) {
  vec2 dist = _pos - vec2(0.5);
  return 1.-smoothstep(_radius - (_radius * 0.5),
                       _radius + (_radius * 0.5),
                       dot(dist, dist) * 20.0);
}

void main() {
  vec2 pos = gl_FragCoord.xy/resolution.xy;

  // Subtract the inverse of orange from white to get an orange glow
  vec3 color = vec3(circle(pos, 0.8)) - vec3(0.0, 0.25, 0.5);

  gl_FragColor = vec4(color, 0.8);
}
</script>
<script id="player-vertex-shader" type="x-shader/x-vertex">
precision highp float;

attribute vec3 position;
attribute vec3 normal;

uniform mat3 normalMatrix;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;

void main() {
  vec4 pos = modelViewMatrix * vec4(position, 0.25);
  gl_Position = projectionMatrix * pos;
}
</script>

I initialize it in the game load by running:

var vertShader = document.getElementById("player-vertex-shader").text;
var fragShader = document.getElementById("player-fragment-shader").text;
var shader = me.video.shader.createShader(me.video.renderer.compositor.gl, vertShader, fragShader);

This is done after video is initialized, and seems to compile the shader program and load fine. The shader also seems to work fine when loading it up in shaderfrog.com and other similar sites.

The problem is, it's leaving me with a totally black screen until I move the character and it redraws. I've read over the webgl fundamentals site, and it seems what I'm missing is binding the character position to the GL buffer.

How do I do this in melonjs.

1

There are 1 best solutions below

0
On

Hi, I wrote the original WebGL compositor for melonJS.

tl;dr: Force the frame to redraw by returning true from your character's entity.update() method. (Or alternatively, increase the animation frame rate to match the game frame rate.)

Example overriding the update method:

update: function (dt) {
    this._super(me.Entity, "update", [dt]);
    return true;
}

This allows the update to continue operating normally (e.g. updating animation state, etc.) but returning true to force the frame to redraw every time.


It might help to understand how the compositor works, and how your shader is interacting with melonJS entities. This describes the inner workings of WebGL integration with melonJS. In short, there is no explicit step to bind positions to the shader. Positions are sent via the vertex attribute buffer, which is batched up (usually for an entire frame) and sent as one big array to WebGL.

The default compositor can be replaced if you need more control over building the vertex buffer, or if you want to do other custom rendering passes. This is done by passing a class reference to me.video.init in the options.compositor argument. The default is me.WebGLRenderer.Compositor:

me.video.init(width, height, {
    wrapper: "screen",
    renderer : me.video.WEBGL,
    compositor: me.WebGLRenderer.Compositor
});

During the draw loop, the default compositor adds a new quad element to the vertex attribute array buffer for every me.WebGLRenderer.drawImage call. This method emulates the DOM canvas method of the same name. The implementation is very simple; it just converts the arguments into a quad and calls the compositor's addQuad method. This is where the vertex attribute buffer is actually populated.

After the vertex attribute buffer has been completed, the flush method is called, which sends the vertex buffer to the GPU with gl.drawElements.

melonJS takes drawing optimization to the extreme. Not only does it batch like-renderables to reduce the number of draw calls (as described above) but it also doesn't send any draw calls if there is nothing to draw. This condition occurs when the frame is identical to the last frame drawn. For example, no entity has moved, the viewport has not scrolled, idle animations have not advanced to the next state, on-screen timer has not elapsed a full second, etc.

It is possible to force the frame to redraw by having any entity in the scene return true from its update method. This is a signal to the game engine that the frame needs to be redrawn. The process is described in more detail on the wiki.