How does "vertexShaderSource" option in MaterialAppearance work?

1. A concise explanation of the problem you're experiencing.
The document says I can use vertexShaderSource option in MaterialAppearance, but I can't make it work.

2. A minimal code example. If you've found a bug, this helps us reproduce and repair it.
js code:

   computeAppearance = new Cesium.MaterialAppearance({
            material: windField,
            vertexShaderSource: Util.getShaderCode('glsl/fullscreen.vert'),
            fragmentShaderSource: Util.getShaderCode('glsl/update.frag')
        });

        fullscreenQuad = new Cesium.GeometryInstance({
            // according to the source code of Cesium.PlaneGeometry
            // o----o (0.5,0.5)
            // | |
            // | |
            // (-0.5,-0.5) o----o
            geometry: new Cesium.PlaneGeometry()
        });

        computePrimitive = new Cesium.Primitive({
            geometryInstances: fullscreenQuad,
            appearance: computeAppearance
        });

vertex shader code:

attribute vec3 position;
attribute vec2 st;
attribute float batchId;

varying vec2 textureCoordinate;

void main() {
    textureCoordinate = st;
    gl_Position = vec4(position.xy, 0.0, 1.0);
}

fragment shader code:

uniform sampler2D U;// (lon, lat*lev)
uniform sampler2D V;// (lon, lat*lev)
uniform vec3 windFieldDimensions;// (lon, lat, lev)
uniform vec3 windFieldSteps;// step of each dimension
uniform sampler2D particles;

varying vec2 textureCoordinate;

void main() {
    vec4 texel = texture2D(particles, textureCoordinate); // texture coordinate must be normalized
    
  vec3 position = texel.rgb;// (lon, lat, lev)
  vec3 windFieldIndex = position;
  windFieldIndex.x = windFieldIndex.x/windFieldSteps.x;
  windFieldIndex.y = windFieldIndex.y/windFieldSteps.y;
  windFieldIndex.z = windFieldIndex.z/windFieldSteps.z;
    
  vec2 textureIndex = vec2(windFieldIndex.x, windFieldIndex.y * windFieldDimensions.y + windFieldIndex.z);
  // quick and dirty estimation for unit conversion: longitude latitude degrees -> meters
  float u = texture2D(U, textureIndex).r / (111111.0 * cos(position.x));
  float v = texture2D(V, textureIndex).r / 111111.0;
  float w = 0.0;
  vec3 windVector = vec3(u, v, w);
  
    vec3 nextPosition = position + windVector;
    gl_FragColor = vec4(nextPosition, 1.0);
}

3. Context. Why do you need to do this? We might know a better way to accomplish your goal.
I need to perform particle computation in GPU. You can see the full problem context in my previous threads:
https://groups.google.com/forum/#!topic/cesium-dev/gjjd9TNeY2A
https://groups.google.com/forum/#!topic/cesium-dev/vR7H4KJURJ8

4. The Cesium version you're using, your operating system and browser.
Cesium 1.53

The error I got:
DeveloperError: Appearance/Geometry mismatch. The appearance requires vertex shader attribute input 'position', which was not computed as part of the Geometry. Use the appearance's vertexFormat property when constructing the geometry.
Error
    at new DeveloperError (http://localhost:5500/lib/CesiumUnminified/Cesium.js:540:19)
    at validateShaderMatching (http://localhost:5500/lib/CesiumUnminified/Cesium.js:99970:27)
    at createShaderProgram (http://localhost:5500/lib/CesiumUnminified/Cesium.js:100477:9)
    at Primitive.update (http://localhost:5500/lib/CesiumUnminified/Cesium.js:100832:13)
    at PrimitiveCollection.update (http://localhost:5500/lib/CesiumUnminified/Cesium.js:132648:27)
    at updateAndRenderPrimitives (http://localhost:5500/lib/CesiumUnminified/Cesium.js:235805:27)
    at executeCommandsInViewport (http://localhost:5500/lib/CesiumUnminified/Cesium.js:235644:13)
    at updateAndExecuteCommands (http://localhost:5500/lib/CesiumUnminified/Cesium.js:235452:13)
    at render (http://localhost:5500/lib/CesiumUnminified/Cesium.js:236092:9)
    at tryAndCatchError (http://localhost:5500/lib/CesiumUnminified/Cesium.js:236112:13)

It looks like the problem is that when using PlaneGeometry, position is actually stored in two attributes. Instead of one vec3 position, it’s position3DHigh and position3DLow. You can see this if you’re using the unminified/source and print attributeLocations in the validateShaderMatching function in Primitive.js. CesiumJS does this since most geometry types are displayed on the globe which can have very large ranges of values. Otherwise you’d get precision errors at this global scale. Here was the Sandcastle I used to test this just in case anyone else wants to test this. So you’d have to define these two attributes instead and use them to get the position.

If you’re simply trying to render something as a full screen quad, you can use the viewport quad instead:

https://github.com/AnalyticalGraphicsInc/cesium/blob/master/Source/Renderer/Context.js#L1184

Here’s an example of setting up a custom primitive that will pass this viewport quad draw command directly to the frameState to render it. It renders in the OVERLAY pass to ensure it’s above everything else.

Here is an example of setting a custom blend mode to allow the overlay to blend with the underlying scene and passing in a dynamic uniform.

1 Like

Thank you, your examples are very helpful!

viewportQuad works well for my particles computation, but it seems that viewportQuad only supports custom fragment shader.

I also need to use custom vertexShaderSource for another render(I should have mentioned this before, sorry), that is why I tried MaterialAppearance, which have both "vertexShaderSource" and "fragmentShaderSource" options.

After the particle computation, I want to get the result of computation and render the particles.

Here is my vertex shader code:

uniform sampler2D particles;

vec3 convertCoordinate(vec3 lonLatLev) {
  vec3 cartesian = vec3(0.0);
  float R = 6371.0 * 1000.0;
  cartesian.x = R * cos(lonLatLev.y) * cos(lonLatLev.x);
  cartesian.y = R * cos(lonLatLev.y) * sin(lonLatLev.x);
  cartesian.z = R *sin(lonLatLev.y);
  return cartesian;
}

void main() {
  vec2 particleIndex = vec2(position.x, position.y);
    vec3 particlePosition = texture2D(particles, particleIndex).rgb;
  particlePosition = convertCoordinate(particlePosition);
  
  vec4 cesiumPosition = vec4(particlePosition, 1.0);
  cesiumPosition = cesiumProjection * cesiumView * cesiumPosition;
  cesiumPosition = normalize(cesiumPosition);
  
    gl_PointSize = 2.0;
    gl_Position = vec4(cesiumPosition.xyz, 1);
}

And my fragment shader code:

void main() {
  vec3 color = vec3(1.0, 0.0, gl_FragCoord.z);
    gl_FragColor = vec4(color, 1.0);
}

Another problem is how can I make particle computation render to a custom framebuffer, but I think it should be discussed later in another new thread?

Hi, Omar, I posted a question about render to texture in a new thread.
https://groups.google.com/forum/#!topic/cesium-dev/gpgLjYyNFKE
Could we begin to discuss about "render to texture" in that thread?

@Omar I tried using your sandcastles to investigate this functionality as well, but they now generate errors.

RuntimeError: Fragment shader failed to compile. Compile log: ERROR: 0:16: ‘varying’ : Illegal use of reserved word

I was wondering if new sandcastles could be provided which work.

1 Like

Me too, any docs on the changes?

1 Like