Any tourial on custom shader?

1. A concise explanation of the problem you're experiencing.
I want to apply custom shaders on 3Dtileset and 3D model, and is looking for tourial on custom shader usage.

I have searched the forum and found many infomation about that:

I. It's possible: https://cesiumjs.org/forum/#!msg/cesium-dev/gSc0w3ZfsM4/lzquFvpFAwAJ
II. Maybe I need to dig into the source code of cesium shader/material pipeline to make a custom shader work. I'm not sure about this.
III. I found something directly related to shader in `MaterialAppearance`(`vertexShaderSource` and `fragmentShaderSource`), but I failed to find shader code in the example or the *Cesium Sandcastle Material Appearance Demo* in https://cesiumjs.org/Cesium/Build/Documentation/MaterialAppearance.html

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

(none)

3. Context. Why do you need to do this? We might know a better way to accomplish your goal.

I need to write custom shaders for custom water, particle-based cloud or mist wich can be placed at any position.

The final scene is like this: http://map.baidu.com/scenic3d/index.html?id=qinghuiyuan
But the dark areas around the center light part will be replaced with water (acts as the ocean).
result illustration: https://i.imgur.com/wB7HXaV.jpg

4. The Cesium version you're using, your operating system and browser.
Cesium version: 1.35
operating system: win10 x64
browser: Google Chrome 63.0.3239.132

Those effects do sound pretty specialized, so I’m not sure if any of the existing classes will really meet your needs. In the end you might have to create new primitive types that control their WebGL resources directly.

But… before going that far you should see if you can use a particle system for the mist and use a custom water material on the globe.

Check out these demos:

Particle systems:

Globe materials:

Thanks for your reply!

But we also need to implement:

  1. animated layer-mist: uv animation, need to pass time as a uniform
  2. color adjust: HSV/RGB parameters, need to pass some parameters as uniforms
  3. water: we need a better and more realistic water compared to the one provided by cesium, for example, reflection and random waves.

  4. So we really need a proper solution to create custom shader and use it with entities/primitives.

I’m not sure if any of the existing classes will really meet your needs. In the end you might have to create new primitive types that control their WebGL resources directly.

How about MaterialAppearance? I saw something in it related to shaders

In the end you might have to create new primitive types that control their WebGL resources directly.
Please enlighten me about that! How should I achieve that? Any links or articles?

You could use an Appearance that has a custom shader. Generally an appearance is attached to a geometry (boxes, spheres, planes, etc). You may need to write your own geometry type if the built-in geometries aren’t suitable.

A few more links to check out:


The globe materials demo also has examples of using a material with custom shaders.

If you decide to make your own primitive type that controls its own WebGL resources, you might want to look at these threads:

https://groups.google.com/d/topic/cesium-dev/-m4JB1IE6bI/discussion

https://groups.google.com/d/topic/cesium-dev/o1ZOdDs6hVg/discussion

However I should warn that this approach is quite low level so there is very little documentation. But if you decide to go that route you may want to review some of Cesium’s source code like Model.js or BillboardCollection.js just to see how WebGL resources and shaders are created.

Good luck!

Thanks for the quick reply and links!

I have looked into fabric, it is really powerful in manipulating pixel colors. But I think it lacks the ability to change vertex data like texcoord(uv), which is required so as to make uv animation.

Did I missed something about that? Could fabric be used to change vertex data such as texcoord? Or does Cesium provided some other method to do that?

I have found a way via MaterialAppearance.

code:

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

var scene = viewer.scene;

var fogMaterial = new Cesium.Material({

strict: true,

translucent: true,

fabric: {

materials: {

alphaMaterial: {

type: ‘AlphaMap’,

uniforms: {

image: ‘img/fog02.png’,

channel: ‘a’

}

}

},

components: {

diffuse: ‘vec3(1.0)’,

alpha: ‘alphaMaterial.alpha’

}

},

});

var vertexShaderSource = `

attribute vec3 position3DHigh;

attribute vec3 position3DLow;

attribute vec3 normal;

attribute vec2 st;

attribute float batchId;

varying vec3 v_positionEC;

varying vec3 v_normalEC;

varying vec2 v_st;

void main()

{

vec4 p = czm_computePosition();

v_positionEC = (czm_modelViewRelativeToEye * p).xyz;      // position in eye coordinates

v_normalEC = czm_normal * normal; // normal in eye coordinates

v_st = st;

v_st = vec2(st.y,st.x);

gl_Position = czm_modelViewProjectionRelativeToEye * p;

}

`;

var fragmentShaderSource = `

varying vec3 v_positionEC;

varying vec3 v_normalEC;

varying vec2 v_st;

void main()

{

vec3 positionToEyeEC = -v_positionEC;

vec3 normalEC = normalize(v_normalEC);;

#ifdef FACE_FORWARD

normalEC = faceforward(normalEC, vec3(0.0, 0.0, 1.0), -normalEC);

#endif

czm_materialInput materialInput;

materialInput.normalEC = normalEC;

materialInput.positionToEyeEC = positionToEyeEC;

materialInput.st = v_st;

czm_material material = czm_getMaterial(materialInput);

#ifdef FLAT

gl_FragColor = vec4(material.diffuse + material.emission, material.alpha);

#else

gl_FragColor = czm_phong(normalize(positionToEyeEC), material);

#endif

}

`;

var fogMaterialAppearance = new Cesium.MaterialAppearance({

translucent: true,

material: fogMaterial,

vertexShaderSource : vertexShaderSource,

fragmentShaderSource : fragmentShaderSource

});

function createPrimitives(scene) {

rectangle = scene.primitives.add(new Cesium.Primitive({

geometryInstances: new Cesium.GeometryInstance({

geometry: new Cesium.RectangleGeometry({

rectangle: Cesium.Rectangle.fromDegrees(-120.0, 20.0, -60.0, 40.0),

vertexFormat: Cesium.EllipsoidSurfaceAppearance.VERTEX_FORMAT

})

}),

appearance: fogMaterialAppearance

}));

}

createPrimitives(scene);

viewer.zoomTo(viewer.primitives);

The subsequent question is how to pass a custom uniform to the vertex shader. @Sean Lilley Any ideas?

Correct me if I’m wrong Sean, but I don’t think there is a way to pass additional uniforms without modifying the Cesium source code.

Thanks,

Gabby

It does seem that way. Geometries don’t have the same shader customization as materials. You’ll most likely need to create your own geometry type.

markdown preview on gist: https://gist.github.com/zwcloud/182c2e2c23ad1aef4b2dd9e6df4c1ea7

(I hate the google group editor.)

raw-text markdown below:

I have created a custom geometry.

Cesium-1.41\Source\Workers\createTetrahedronGeometry.js


define([

'../Core/TetrahedronGeometry',

'../Core/defined',

], function(

TetrahedronGeometry,

defined) {

"use strict";

function createTetrahedronGeometry(tetrahedronGeometry, offset) {

if (defined(offset)) {

tetrahedronGeometry = TetrahedronGeometry.unpack(tetrahedronGeometry, offset);

}

return TetrahedronGeometry.createGeometry(tetrahedronGeometry);

}

return createTetrahedronGeometry;

});

Cesium-1.41\Source\Core\TetrahedronGeometry.js


define([

'./Cartesian3',

'./ComponentDatatype',

'./PrimitiveType',

'./BoundingSphere',

'./GeometryAttribute',

'./GeometryAttributes',

'./GeometryPipeline',

'./VertexFormat',

'./Geometry'

], function(

Cartesian3,

ComponentDatatype,

PrimitiveType,

BoundingSphere,

GeometryAttribute,

GeometryAttributes,

GeometryPipeline,

VertexFormat,

Geometry) {

"use strict";

var TetrahedronGeometry = function() {

this._workerName = 'createTetrahedronGeometry';

};

TetrahedronGeometry.createGeometry = function() {

var negativeRootTwoOverThree = -Math.sqrt(2.0) / 3.0;

var negativeOneThird = -1.0 / 3.0;

var rootSixOverThree = Math.sqrt(6.0) / 3.0;

var positions = new Float64Array(4 * 3 * 3);

// back triangle

positions[0] = 0.0;

positions[1] = 0.0;

positions[2] = 1.0;

positions[3] = 0.0;

positions[4] = (2.0 * Math.sqrt(2.0)) / 3.0;

positions[5] = negativeOneThird;

positions[6] = -rootSixOverThree;

positions[7] = negativeRootTwoOverThree;

positions[8] = negativeOneThird;

// left triangle

positions[9] = 0.0;

positions[10] = 0.0;

positions[11] = 1.0;

positions[12] = -rootSixOverThree;

positions[13] = negativeRootTwoOverThree;

positions[14] = negativeOneThird;

positions[15] = rootSixOverThree;

positions[16] = negativeRootTwoOverThree;

positions[17] = negativeOneThird;

// right triangle

positions[18] = 0.0;

positions[19] = 0.0;

positions[20] = 1.0;

positions[21] = rootSixOverThree;

positions[22] = negativeRootTwoOverThree;

positions[23] = negativeOneThird;

positions[24] = 0.0;

positions[25] = (2.0 * Math.sqrt(2.0)) / 3.0;

positions[26] = negativeOneThird;

// bottom triangle

positions[27] = -rootSixOverThree;

positions[28] = negativeRootTwoOverThree;

positions[29] = negativeOneThird;

positions[30] = 0.0;

positions[31] = (2.0 * Math.sqrt(2.0)) / 3.0;

positions[32] = negativeOneThird;

positions[33] = rootSixOverThree;

positions[34] = negativeRootTwoOverThree;

positions[35] = negativeOneThird;

var indices = new Uint16Array(4 * 3);

// back triangle

indices[0] = 0;

indices[1] = 1;

indices[2] = 2;

// left triangle

indices[3] = 3;

indices[4] = 4;

indices[5] = 5;

// right triangle

indices[6] = 6;

indices[7] = 7;

indices[8] = 8;

// bottom triangle

indices[9] = 9;

indices[10] = 10;

indices[11] = 11;

var attributes = new GeometryAttributes({

position : new GeometryAttribute({

componentDatatype : ComponentDatatype.DOUBLE,

componentsPerAttribute : 3,

values : positions

})

});

return new Geometry({

attributes : attributes,

indices : indices,

primitiveType : PrimitiveType.TRIANGLES,

boundingSphere : new BoundingSphere(new Cartesian3(0.0, 0.0, 0.0), 1.0)

});

};

TetrahedronGeometry.unpack = function(array, startingIndex, result) {

return new TetrahedronGeometry();//do nothing but return a defualt TetrahedronGeometry

}

return TetrahedronGeometry;

});

Then in the Sandcastle app:


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

var scene = viewer.scene;

var ellipsoid = scene.globe.ellipsoid;

var modelMatrix = new Cesium.Matrix4();

Cesium.Matrix4.multiplyByTranslation(

Cesium.Transforms.eastNorthUpToFixedFrame(

ellipsoid.cartographicToCartesian(Cesium.Cartographic.fromDegrees(-100.0, 40.0))

),

new Cesium.Cartesian3(0.0, 0.0, 200000.0),

modelMatrix);

Cesium.Matrix4.multiplyByUniformScale(modelMatrix, 500000.0, modelMatrix);

var instance = new Cesium.GeometryInstance({

geometry : new Cesium.TetrahedronGeometry(),

modelMatrix : modelMatrix,

attributes : {

color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.WHITE)

}

});

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

geometryInstances : instance,

appearance : new Cesium.PerInstanceColorAppearance({

flat : true,

translucent : false

})

}));

result:

Now the next problem is: how to apply custom shader with uniforms to it?

Just luckily came across a thread by Patrick Cozzi: https://cesiumjs.org/forum/#!msg/cesium-dev/NyQ-woOhWtU/r2sZ5VKJgzAJ

With Cesium, there’s a few options for custom rendering. From most common (and easiest) to less common:

  • Create geometries for your data from Cesium’s large geometry collection (tutorial). This doesn’t require any WebGL.
  • Create custom geometries, basically vertex and index buffers, and appearances, basically shaders and render state (partial draft tutorial). Mix and match your own with Cesium’s collection. This can require writing GLSL shaders, but the other WebGL features are abstracted.
  • Implement a Cesium primitive, which has full access to the Cesium renderer - including vertex arrays, buffers, textures, render state, shader programs, framebuffers, etc. A simple example is the EllipsoidPrimitive.
    Even the last option above doesn’t use WebGL directly, except for writing GLSL shaders. Modern engines, including Cesium, abstract the graphics API, which makes it easier to use and generally faster since it allows the engine to centralize optimizations like minimizing GL state changes. Porting your OpenGL code to use the Cesium renderer should be pretty easy. If your case is simple enough, creating or using existing geometries and appearances will be the easiest approach.

If you can tell us more about how your currently using OpenGL, I can provide more concrete advice.

Patrick

You guys really need to add the above into the official tutorial!

What I need to do is the last one: Implement a Cesium primitive, while creating your own geometry type is still too high-level. I’m working on it now.

Thanks for pointing out specifically what you would like to see in the learning materials! We’ll keep this in mind when creating/updating tutorials.

I also met the question.i need write my shader ,Can you tell me some code example or tutorial.Thank you.

在 2018年1月12日星期五 UTC+8下午4:32:30,zwcloud写道:

If you post a new question with some more context and what you are trying to accomplish, we’ll help as best we can!