Import webgl code to Cesium

Hi,

I’m trying to import pure webgl code to Cesium, but I don’t know what is the correct way.

With my webgl code is able to create a animation between 2 textures. For this purpose I define shaders (vertex and fragment). At first of all I’m trying to do some tests with createShaderProgram of Context.js (http://cesiumjs.org/Cesium/Build/Documentation/Context.html):

// Example 2. Create a shader program with explicit attribute indices.

var vs = ``'attribute vec4 position;' +

``'attribute vec3 normal;' +

``'void main() { gl_Position = position; }'``;

var fs = ``'void main() { gl_FragColor = vec4(1.0); }'``;

var attributes = {

``position : 0,

``normal : 1

};

sp = context.createShaderProgram(vs, fs, attributes);

But when I launched the program it didn’t do anything and in console appear this error:

But Uncaught ReferenceError: Context is not defined

I attached full file.

¿How is the correct way to use this code? ¿How can I create and use shader program?¿Do you know any simple example which work like the documentation example?

Thanks,

Aritz

test.html (1.65 KB)

If you want to use Cesium's scene context, I think you need to use the proper namespace:

var context = scene.context;

If you want to instantiate your own context, try

var context = new Cesium.Context(...)

Thank you!!

With var context = scene.context; fixed the error.

Somebody know any simple example about createShaderProgram? After created that shader what steps I need to follow?

Regards,

Aritz

Aritz,

For most use cases, we do not need to do custom rendering. If we just want custom vertex/fragment shaders, consider writing an appearance, which will then work with Cesium’s geometries.

To get started, copy and paste the following into Sandcastle:

require([‘Cesium’], function(Cesium) {
“use strict”;

var viewer = new Cesium.Viewer('cesiumContainer');
var scene = viewer.scene;
var ellipsoid = viewer.centralBody.ellipsoid;

var ExampleAppearance = function() {
    this.material = undefined;

    this.vertexShaderSource =

‘attribute vec3 position3DHigh;’ +
‘attribute vec3 position3DLow;’ +
‘attribute vec3 normal;’ +
‘varying vec3 v_positionEC;’ +
‘varying vec3 v_normalEC;’ +
‘void main()’ +
‘{’ +
’ vec4 p = czm_computePosition();’ +
’ v_positionEC = (czm_modelViewRelativeToEye * p).xyz;’ +
’ v_normalEC = czm_normal * normal;’ +
’ gl_Position = czm_modelViewProjectionRelativeToEye * p;’ +
‘}’;

    this.fragmentShaderSource =

‘varying vec3 v_positionEC;’ +
‘varying vec3 v_normalEC;’ +
‘void main()’ +
‘{’ +
’ gl_FragColor = vec4(v_normalEC, 0.5);’ +
‘}’;
this.renderState = Cesium.Appearance.getDefaultRenderState(true, false);
};

ExampleAppearance.prototype.getFragmentShaderSource = Cesium.Appearance.prototype.getFragmentShaderSource;
ExampleAppearance.prototype.isTranslucent = Cesium.Appearance.prototype.isTranslucent;
ExampleAppearance.prototype.getRenderState = Cesium.Appearance.prototype.getRenderState;

// Red extent
var extent = Cesium.Extent.fromDegrees(-180.0, -90.0, 180.0, 90.0);
var redExtentInstance = new Cesium.GeometryInstance({
    geometry : new Cesium.ExtentGeometry({
        extent : extent,
        vertexFormat : Cesium.VertexFormat.POSITION_AND_NORMAL
    })
});

// Add extent instances to primitives
scene.primitives.add(new Cesium.Primitive({
    geometryInstances : [redExtentInstance],
    appearance : new ExampleAppearance()
}));

Sandcastle.finishedLoading();

});

Patrick

Thank you very much PatricK!!!

It looks very good.

Now I’m trying to put my own shaders code and this error appear:

DeveloperError: Appearance/Geometry mismatch. The appearance requires vertex shader attribute input ‘aTextureCoord’, which was not computed as part of the Geometry. Use the appearance’s vertexFormat property when constructing the geometry.
Error

How can I manage the different type of attributes of the shaders? I watch this http://cesiumjs.org/Cesium/Build/Documentation/VertexFormat.html?classFilter=vertexFormat&show=all in documentation but I don’t know how to use it.

These are my attributes:
’ attribute vec3 aVertexPosition;\n’+
’ attribute vec2 aTextureCoord;\n’+

Regards,

Aritz

Attributes are matched by name and geometries do not have attributes called aVertexPosition and aTextureCoord. Instead use

attribute vec3 position3DHigh;

attribute vec3 position3DLow;

attribute vec2 st;

For example, see

https://github.com/AnalyticalGraphicsInc/cesium/blob/master/Source/Shaders/Appearances/AllMaterialAppearanceVS.glsl

Patrick

Thanks for your reply Patrick!!

Is possible to link or pass a texture to shader? Something like:
gl_FragColor = vec4(v_normalEC, 0.5) + texture2D(textureName, texPos);

Or how is the correct way to do a gl.bindTexture with shaders in CESIUM?

Regards,

Aritz

Yes. Use an existing material or create a new material (which can integrate with your appearance or the existing MaterialAppearance). For more on materials, see

https://github.com/AnalyticalGraphicsInc/cesium/wiki/Fabric

Patrick

Ok Patrick, I use a test image and the code is here:

var widget = new Cesium.CesiumWidget(‘cesiumContainer’);
var scene = widget.scene;
var ellipsoid = widget.centralBody.ellipsoid;

var ExampleAppearance = function() {
this.material = undefined;

    this.vertexShaderSource =

‘attribute vec3 position3DHigh;’ +
‘attribute vec3 position3DLow;’ +
‘attribute vec3 normal;’ +
‘varying vec3 v_positionEC;’ +
‘varying vec3 v_normalEC;’ +
‘attribute vec2 st;’ +
‘void main()’ +
‘{’ +
’ vec4 p = czm_computePosition();’ +
’ v_positionEC = (czm_modelViewRelativeToEye * p).xyz;’ +
’ v_normalEC = czm_normal * normal;’ +
’ gl_Position = czm_modelViewProjectionRelativeToEye * p;’ +
‘}’;

    this.fragmentShaderSource =

‘varying vec3 v_positionEC;’ +
‘varying vec3 v_normalEC;’ +
‘void main()’ +
‘{’ +
’ gl_FragColor = vec4(v_normalEC, 0.5);’ +
‘}’;
this.renderState = Cesium.Appearance.getDefaultRenderState(true, false);
};

ExampleAppearance.prototype.getFragmentShaderSource = Cesium.Appearance.prototype.getFragmentShaderSource;
ExampleAppearance.prototype.isTranslucent = Cesium.Appearance.prototype.isTranslucent;
ExampleAppearance.prototype.getRenderState = Cesium.Appearance.prototype.getRenderState;

// Red extent
var extent = Cesium.Extent.fromDegrees(-180.0, -90.0, 180.0, 90.0);
var redExtentInstance = new Cesium.GeometryInstance({
    geometry : new Cesium.ExtentGeometry({
        extent : extent,
        vertexFormat : Cesium.VertexFormat.POSITION_AND_NORMAL
    })
});

scene.primitives.add(new Cesium.ExtentPrimitive({
extent : Cesium.Extent.fromDegrees(-95.0, 20.0, -90.0, 25.0),
material : new Cesium.Material({
fabric : {
    type : 'Image',
    uniforms : {
      image : 'img/Cesium_Logo_overlay.png',
    }
    }
})

}));

// Add extent instances to primitives
scene.primitives.add(new Cesium.Primitive({
    geometryInstances : [redExtentInstance],
    appearance : new ExampleAppearance()
}));

But if I want to link or pass a texture to shader I don’t know how can I do. I read your shared links but there isn’t example to show me what is the way. I’m a beginner in this kind of topics.

Thanks,

Aritz

When I try to link texture in shader this error appear:
[GL] Fragment shader compile log: ERROR: 0:? : ‘texture2D’ : no matching overloaded function found

I don’t know what but I’m doing somthing wrong. Somebody know how can I link or pass correctly a texture to a shader? Somebody know any simple example about it?

Best regards,

Aritz

Hi Aritz,

Can you post the code that is causing the error?

For how to use ‘texture2D’ , see the Texture Lookup Functions section of the GLSL ES 2.0 Specification.

-Dan

Hello Dan,
The error was that I passed a vec3 parameter instead of vec2 -> * vec4 texture2D (sampler2D sampler, vec3 coord*) insted of vec4 texture2D (sampler2D sampler, vec2 coord)
Now I have new problems.
[GL] Vertex shader compile log: ERROR: 0:1: ‘assign’ : cannot convert from ‘-component vector of float’ to ‘varying highp -component vector of float’
I´m not sure if I followed the right steps for my purpose. Starting from Patrick’s example I try to create new material in my case an image from 2 different ways, one with scene.primitives.add(new Cesium.Primitive({ and the other with var imageExtentInstance = new Cesium.GeometryInstance({ . After I wanted to link or pass this texture to the shader.
Here the code:
_var widget = new Cesium.CesiumWidget(‘cesiumContainer’);
var scene = widget.scene;
var ellipsoid = widget.centralBody.ellipsoid;
var ExampleAppearance = function() {
//this.material = undefined;
this.material = new Cesium.Material.fromType(‘Image’);
this.material.uniforms.image = ‘img/Cesium_Logo_overlay.png’;
this.vertexShaderSource =
‘attribute vec3 position3DHigh;’ +
‘attribute vec3 position3DLow;’ +
‘attribute vec3 normal;’ +
‘varying vec3 v_positionEC;’ +
‘varying vec3 v_normalEC;’ +
‘attribute vec2 st;’ +
‘uniform sampler2D image;’ +
‘varying vec4 Position;\n’+
‘void main()’ +
‘{’ +
’ Position = (st,st);’+
’ vec2 texPos = Position.xy;\n’+
’ vec4 p = czm_computePosition();’ +
’ v_positionEC = (czm_modelViewRelativeToEye * p).xyz;’ +
’ v_normalEC = czm_normal * normal;’ +
’ gl_Position = czm_modelViewProjectionRelativeToEye * p;’ +
‘}’;
this.fragmentShaderSource =
‘varying vec3 v_positionEC;’ +
‘varying vec3 v_normalEC;’ +
‘uniform sampler2D image;’ +
‘varying vec4 Position;\n’+
‘void main()’ +
‘{’ +
’ vec2 texPos = Position.xy;\n’+
’ gl_FragColor = vec4(v_normalEC, 0.5) + texture2D(image, texPos);’ +
‘}’;
this.renderState = Cesium.Appearance.getDefaultRenderState(true, false);
};
ExampleAppearance.prototype.getFragmentShaderSource = Cesium.Appearance.prototype.getFragmentShaderSource;
ExampleAppearance.prototype.isTranslucent = Cesium.Appearance.prototype.isTranslucent;
ExampleAppearance.prototype.getRenderState = Cesium.Appearance.prototype.getRenderState;

var imageExtentInstance = new Cesium.GeometryInstance({
    geometry : new Cesium.ExtentGeometry({
        extent : Cesium.Extent.fromDegrees(-95.0, 20.0, -90.0, 25.0),
        vertexFormat : Cesium.VertexFormat.POSITION_AND_NORMAL,
        appearance : new Cesium.MaterialAppearance({
            material : new Cesium.Material({
                fabric : {
                    type : 'Image',
                    uniforms : {
                      image : 'img/Cesium_Logo_overlay.png',
                    }
                    }
            })
        })
    })
});
var mapExtentInstance = new Cesium.GeometryInstance({
    geometry : new Cesium.ExtentGeometry({
        extent : Cesium.Extent.fromDegrees(-180.0, -90.0, 180.0, 90.0),
        vertexFormat : Cesium.PerInstanceColorAppearance.POSITION_AND_NORMAL
        }),
});
// Add extent instances to primitives
scene.primitives.add(new Cesium.Primitive({
    geometryInstances : [imageExtentInstance,mapExtentInstance],
    appearance : new ExampleAppearance()
       
    }));   

scene.primitives.add(new Cesium.ExtentPrimitive({
extent : Cesium.Extent.fromDegrees(-100.0, 20.0, -90.0, 25.0),
material : new Cesium.Material({
fabric : {
type : ‘Image’,
uniforms : {
image : ‘img/Cesium_Logo_overlay.png’,
}
}
})
}));_

What is the correct way to create a new material? and to pass or link texture to shader?

Best regards,

Aritz

Aritz,

Can you explain what you are trying to do? If I understand that I might be able to tell you the best way to get it done.

The error is on the first line of main in your shader source. It should be:

Position = vec4(st, st); // or Position = st.xyxy;

Here is an example of a custom material:

var materialSource =

‘uniform sampler2D image;\n’ +

‘czm_material czm_getMaterial(czm_materialInput materialInput)\n’ +

‘{\n’ +

’ czm_material material = czm_getDefaultMaterial(materialInput);\n’ +

’ material.diffuse = texture2D(image, materialInput.st);\n’ +

’ return material;\n’ +

‘}\n’;

var material = new Material({

fabric : {

uniforms : {

image : url

},

source : materialSource

}

});

The definition of czm_materialInput and czm_material are here:

https://github.com/AnalyticalGraphicsInc/cesium/blob/master/Source/Shaders/Builtin/Structs/materialInput.glsl

https://github.com/AnalyticalGraphicsInc/cesium/blob/master/Source/Shaders/Builtin/Structs/material.glsl

You can create an extent to test your material like in the code for this example:

http://cesiumjs.org/Cesium/Apps/Sandcastle/index.html?src=Ellipsoid%20Surface.html&label=Appearances

Dan

Hello Dan,
Thank you for your response!!
I have webgl code which make animation with 3 images, two images are sea textures and the third one is a noise texture. With the cnoise and pnoise functions I make the animation in fragment shader, like a sea water flow animation. This animation is only a test to understood animations functionality, and in the future I want to make a animations with external data servers like a WMS or THREDDS.
With your example code I tried to make an custom material, but Material is not defined error appear:
var materialSource =
‘uniform sampler2D image;\n’ +
‘czm_material czm_getMaterial(czm_materialInput materialInput)\n’ +
‘{\n’ +
’ czm_material material = czm_getDefaultMaterial(materialInput);\n’ +
’ material.diffuse = texture2D(image, materialInput.st);\n’ +
’ return material;\n’ +
‘}\n’;
var testMaterial = new Material({ -> Uncaught ReferenceError: Material is not defined
fabric : {
uniforms : {
image : ‘img/Cesium_Logo_overlay.png’
},
source : materialSource
}
});

scene.primitives.add(new Cesium.ExtentPrimitive({
extent : Cesium.Extent.fromDegrees(-95.0, 20.0, -90.0, 25.0),
fabric : {
type : ‘testMaterial’
}
}));

What is the correct way to do that?

Best regards,

Aritz

Hi Aritz,

Here is a working Sandcastle example:

require([‘Cesium’], function(Cesium) {

“use strict”;

var viewer = new Cesium.Viewer(‘cesiumContainer’);

var scene = viewer.scene;

var primitives = scene.primitives;

var ellipsoid = viewer.centralBody.ellipsoid;

var materialSource =

‘uniform sampler2D image;\n’ +

‘czm_material czm_getMaterial(czm_materialInput materialInput)\n’ +

‘{\n’ +

’ czm_material material = czm_getDefaultMaterial(materialInput);\n’ +

’ material.diffuse = texture2D(image, materialInput.st).rgb;\n’ +

’ return material;\n’ +

‘}\n’;

var testMaterial = new Cesium.Material({

fabric : {

uniforms : {

image : ‘…/images/Cesium_Logo_Color.jpg’

},

source : materialSource

}

});

scene.primitives.add(new Cesium.ExtentPrimitive({

extent : Cesium.Extent.fromDegrees(-95.0, 20.0, -90.0, 25.0),

material : testMaterial

}));

Sandcastle.finishedLoading();

});

I tested it with the head of the master branch on GitHub that will be released tomorrow. You might have to make modifications if you are using a previous version.

You can add the additional two image uniforms to the material and a float uniform for animation time. When the time is updated you can set it with:

testMaterial.uniforms.time = animationTime;

Regards,

Dan

Thank you Dan, it’s work fine!!

When you say that I can add the additional two image uniforms to the material is something like this?

var MaterialSource =
‘uniform sampler2D ucurSampler;\n’ +
‘uniform sampler2D vcurSampler;\n’ +
‘czm_material czm_getMaterial(czm_materialInput materialInput)\n’ +
‘{\n’ +
’ czm_material material = czm_getDefaultMaterial(materialInput);\n’ +
’ material.diffuse = texture2D(ucurSampler, materialInput.st).rgb;\n’ +
’ material.diffuse = texture2D(vcurSampler, materialInput.st).rgb;\n’ +
’ material.alpha = 0.5;\n’ +
’ return material;\n’ +
‘}\n’;

var testMaterial= new Cesium.Material({
fabric : {
uniforms : {
ucurSampler : ‘img/Cesium_Logo_overlay.png’,
vcurSampler : ‘img/vcur.1024x1024.png’
},
source : MaterialSource
}
});

scene.primitives.add(new Cesium.ExtentPrimitive({
extent : Cesium.Extent.fromDegrees(-180.0, -90.0, 180.0, 90.0),
material : testMaterial
}));

With this code I only view one texture, how can I view both of them? How can I define 2 or more material.diffuse? Or I need to create different materials?

Regards,

Aritz

Yes, you can add images like that, but the way MaterialSource is written, only vcurSampler will contribute to the diffuse color because it is the last assigned to material.diffuse. If you want to see both images you need to blend then somehow. You can change the source to something like below to see both textures.

vec4 ucurColor = texture2D(ucurSampler, materialInput.st);

vec4 vcurColor = texture2D(vcurSampler, materialInput.st);

material.diffuse = mix(ucurColor.rgb, vcurColor.rgb, 0.5);

Hello Dan,

And if I want add more than 2 images How can I do? This is the correct way?

var ucurMaterialSource =
‘uniform sampler2D ucurSampler;\n’ +
‘uniform sampler2D vcurSampler;\n’ +
‘uniform sampler2D earthSampler;\n’+
‘czm_material czm_getMaterial(czm_materialInput materialInput)\n’ +
‘{\n’ +
’ czm_material material = czm_getDefaultMaterial(materialInput);\n’ +
’ vec4 ucurColor = texture2D(ucurSampler, materialInput.st);\n’ +
’ vec4 vcurColor = texture2D(vcurSampler, materialInput.st);\n’ +
’ vec4 earthColor = texture2D(earthSampler, materialInput.st);\n’ +
’ vec3 test = mix(ucurColor.rgb, vcurColor.rgb, 0.5);\n’ +
’ material.diffuse = mix(test.rgb, earthColor.rgb, 0.5);\n’ +
’ material.alpha = 0.5;\n’ +
’ return material;\n’ +
‘}\n’;

var ucurMaterial = new Cesium.Material({
fabric : {
uniforms : {
ucurSampler : ‘img/ucur.1024x1024.png’,
vcurSampler : ‘img/vcur.1024x1024.png’,
earthSampler : ‘img/world.200412.3x1024x1024.jpg’,
noiseSampler : ‘img/whiteNoise256.256.jpg’
},
source : ucurMaterialSource
}
});

In the other hand I don’t understand how can I use the testMaterial.uniforms.time = animationTime; Do you know some example?

Best regards,

aritz

Hello Dan,

I managed to create animations by following your advices, thank you very much!!

My
next step is to add material with multple passes. What is the preferred way to do that? Connecting different materials? Or creating a single material with all the shaders?

I tried the first approach: defining two material and trying to link them using the “materials” member of “fabric”.

This is my test code, but it does not work :frowning:

var rttmaterialSource =
‘uniform sampler2D temperatureSampler;\n’ +
‘czm_material czm_getMaterial(czm_materialInput materialInput)\n’ +
‘{\n’ +
’ czm_material material = czm_getDefaultMaterial(materialInput);\n’ +
’ material.diffuse = texture2D(temperatureSampler, materialInput.st).bgr;\n’ + //how create a FBO texture in the first pass?
’ return material;\n’ +
‘}\n’;

var simpleMaterialSource =
‘uniform sampler2D background;\n’ +

'czm_material czm_getMaterial(czm_materialInput materialInput)\n' +
'{\n' +
'    czm_material material = czm_getDefaultMaterial(materialInput);\n' +
'    material.diffuse = texture2D(background, materialInput.st).rgb;\n' +
'    return material;\n' +
'}\n';

//1 pass : blur the input image

var colorInvertedmaterial = new Cesium.Material({
fabric : {
uniforms : {
temperatureSampler : ‘img/pottmp2010occam.2048x2048.png’,
},
source : rttmaterialSource
}
});

// pass screen --> globe

var passScreenMaterial = new Cesium.Material({
fabric : {
uniforms : {
background : colorInvertedmaterial, ///Is posible to asign FBO in this way???
},
source : simpleMaterialSource,
materials : colorInvertedmaterial ///or in this other way???
}
});

scene.primitives.add(new Cesium.ExtentPrimitive({
extent : Cesium.Extent.fromDegrees(-180.0, -90.0, 180.0, 90.0),
material : passScreenMaterial
}));

The “colorInvertedmaterial” material (with the shader defined in “rttmaterialSource”) would be the first pass. In this case we just play with the image component as an example.

The “screenMaterial” material (with the shader defined in “simpleMaterialSource”) just renders the output of the previous shader in the cesium globe.

You may notice we have tried to link them by linking the “background” uniform to the “colorInvertedmaterial” material.

Best regards,

Aritz

Hi Aritz,

Have you seen the wiki page on Fabric?

See the section on combining materials.

Regards,

Dan