663 lines
30 KiB
Markdown
663 lines
30 KiB
Markdown
![]() |
<link rel="stylesheet" href="https://csdnimg.cn/release/blogv2/dist/mdeditor/css/editerView/kdoc_html_views-1a98987dfd.css">
|
|||
|
<link rel="stylesheet" href="https://csdnimg.cn/release/blogv2/dist/mdeditor/css/editerView/ck_htmledit_views-704d5b9767.css">
|
|||
|
|
|||
|
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
|
|||
|
<path stroke-linecap="round" d="M5,0 0,2.5 5,5z" id="raphael-marker-block" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);"></path>
|
|||
|
</svg>
|
|||
|
**一、简介**
|
|||
|
[shadertoy](https://www.shadertoy.com/)是一个基于webgl的分享Shader的开放平台,用户可以在上面根据既定规则分享自己编写的shader。在Cesium中要想实现一些酷炫的效果,唯一的一条路就是写shader,而shader的编写应该算是图形学中的难度天花板了。好在有很多shader大佬分享了自己编写的glsl效果,比如shadertoy网站上就是这些大牛们的作品,我们可以借鉴。
|
|||
|

|
|||
|
|
|||
|
**二、shadertoy着色器基本结构**
|
|||
|
|
|||
|
shadertoy上的shader是纯2d绘图,没有几何顶点这些概念,它的绘图方式和canvas绘图方式很像,它将整个canvas作为绘图的画布,所以输入参数fragCoord的x值范围是(0,画布的宽度),fragCoord的y值范围是(0,画布的高度),画布的宽高在定义的输入参数中是iResolution,所以fragCoord.x范围就是(0,iResolution.x),fragCoord.y范围就是(0,iResolution.y)。
|
|||
|

|
|||
|
|
|||
|
如图所示,shadertoy上的shader示例最基本的着色器结构主要包括两个部分:
|
|||
|
a、输入参数的定义
|
|||
|
b、着色器的入口函数
|
|||
|
|
|||
|
1、输入参数,通过uniform来定义外部的输入值。
|
|||
|
|
|||
|
uniform vec3 iResolution; // 视口分辨率,即画布的宽高
|
|||
|
uniform float iTime; // shader 的运行时间 秒
|
|||
|
uniform float iTimeDelta; // 渲染时间 秒
|
|||
|
uniform float iFrameRate; // shader 帧率
|
|||
|
uniform int iFrame; // shader 帧率
|
|||
|
uniform float iChannelTime[4]; // 频道运行时间 不管
|
|||
|
uniform vec3 iChannelResolution[4]; // 频道分辨率 不管
|
|||
|
uniform vec4 iMouse; // 鼠标坐标
|
|||
|
uniform samplerXX iChannel0..3; // 输入的纹理 比如我们从一张图片上采用颜色
|
|||
|
uniform vec4 iDate; // 日期 年月日 不管
|
|||
|
uniform float iSampleRate; // 声音采样 不管
|
|||
|
|
|||
|
2、入口函数mainImage
|
|||
|
|
|||
|
void mainImage(out vec4 fragColor, in vec2 fragCoord )
|
|||
|
{
|
|||
|
fragColor = vec4(1.);
|
|||
|
}
|
|||
|
|
|||
|
方法的第一个参数fragColor,是一个vec4类型的变量,表示最后输出的颜色值。
|
|||
|
方法的第二个参数fragCoord,是一个vec2类型的变量,表示输入的像素坐标。
|
|||
|
|
|||
|
**三、在cesium中如何使用**
|
|||
|
|
|||
|
shadertoy上的着色器是在一个canvas画布上进行工作的,要移植到Cesium中,我们需要找一个载体来替代canvas。我们知道,Cesium绘制几何图形可以通过Entity和Primitive两种方式,那么只有Primitive+Appearance比较合适了,关于Primitive的使用及介绍,可以观看前面的章节。要将着色器移植到Cesium中,我们先来重点看看shadertoy上的shader着色器需要用到的参数。以下面这个例子讲解https://www.shadertoy.com/view/XdlSDs
|
|||
|

|
|||
|
|
|||
|
glsl代码:
|
|||
|
|
|||
|
void mainImage(out vec4 fragColor, in vec2 fragCoord )
|
|||
|
{
|
|||
|
vec2 p = (2.0*fragCoord.xy-iResolution.xy)/iResolution.y;
|
|||
|
float tau = 3.1415926535*2.0;
|
|||
|
float a = atan(p.x,p.y);
|
|||
|
float r = length(p)*0.75;
|
|||
|
vec2 uv = vec2(a/tau,r);
|
|||
|
|
|||
|
//get the color
|
|||
|
float xCol = (uv.x - (iTime / 3.0)) * 3.0;
|
|||
|
xCol = mod(xCol, 3.0);
|
|||
|
vec3 horColour = vec3(0.25, 0.25, 0.25);
|
|||
|
|
|||
|
if (xCol < 1.0) {
|
|||
|
|
|||
|
horColour.r += 1.0 - xCol;
|
|||
|
horColour.g += xCol;
|
|||
|
}
|
|||
|
else if (xCol < 2.0) {
|
|||
|
|
|||
|
xCol -= 1.0;
|
|||
|
horColour.g += 1.0 - xCol;
|
|||
|
horColour.b += xCol;
|
|||
|
}
|
|||
|
else {
|
|||
|
|
|||
|
xCol -= 2.0;
|
|||
|
horColour.b += 1.0 - xCol;
|
|||
|
horColour.r += xCol;
|
|||
|
}
|
|||
|
|
|||
|
// draw color beam
|
|||
|
uv = (2.0 * uv) - 1.0;
|
|||
|
float beamWidth = (0.7+0.5*cos(uv.x*10.0*tau*0.15*clamp(floor(5.0 + 10.0*cos(iTime)), 0.0, 10.0))) * abs(1.0 / (30.0 * uv.y));
|
|||
|
vec3 horBeam = vec3(beamWidth);
|
|||
|
fragColor = vec4((( horBeam) * horColour), 1.0);
|
|||
|
}
|
|||
|
|
|||
|
a、首先是fragCoord,前面介绍过,fragCoord表示当前处理的像素坐标,是一个vec2类型,fragCoord.x范围为(0,画布宽度),fragCoord.y范围为(0,画布高度)。
|
|||
|
b、其次是iResolution,iResolution代表的是当前画布的宽高,即绘图区域的尺寸,所以fragCoord.x范围就是(0,iResolution.x),fragCoord.y范围就是(0,iResolution.y)。
|
|||
|
c、然后我们还在代码中看到有个iTime,该参数代表当前运行的时间,一般用来实现动画,因为您会发现大多数shader的效果都是动态的。
|
|||
|
d、最后是输出结果fragColor,代表最后计算的颜色输出值,在Cesium中为out\_FragColor。
|
|||
|
|
|||
|
接下来介绍在Cesium如何获取对应的参数:
|
|||
|
|
|||
|
a、fragCoord在Cesium有个gl\_FragCoord与之对应,这是一个WebGL内置的变量。
|
|||
|
b、iResolution在Cesium有个czm\_viewport与之对应,不过使用时采用zw分量即 czm\_viewport.zw
|
|||
|
c、iTime在Cesium中没有对应的变量,我们可以通过变量的方式传递一个参数,然后在渲染时不断修改该值,不过这种方式略显麻烦,在Cesium中我们可以用float iTime=czm\_frameNumber/100.来模拟,czm\_frameNumber代表当前帧,是一个自增长的数值,所以可以用来模拟时间不断地增长。
|
|||
|
|
|||
|
接下来我们实操一下,在Cesium中实现该shader的效果:
|
|||
|
|
|||
|
1、首先我们创建一个Primitive并添加到sene中
|
|||
|
|
|||
|
let xMin = 115.894604, yMin = 39.516896, xMax = 117.431959, yMax = 40.630521;
|
|||
|
let rect = new Cesium.Rectangle(Cesium.Math.toRadians(xMin), Cesium.Math.toRadians(yMin), Cesium.Math.toRadians(xMax), Cesium.Math.toRadians(yMax));
|
|||
|
const rectangle = new Cesium.RectangleGeometry({
|
|||
|
rectangle: rect,
|
|||
|
height: 10000.0,
|
|||
|
});
|
|||
|
const geometry = Cesium.RectangleGeometry.createGeometry(rectangle);
|
|||
|
viewer.scene.primitives.add(new Cesium.Primitive({
|
|||
|
geometryInstances: new Cesium.GeometryInstance({
|
|||
|
geometry: geometry
|
|||
|
}),
|
|||
|
}));
|
|||
|
|
|||
|
2、此时会发现什么也看不见,这是因为没有设置外观,我们创建一个默认的外观
|
|||
|
|
|||
|
let appearance = new Cesium.MaterialAppearance({
|
|||
|
material: new Cesium.Material({
|
|||
|
fabric: {
|
|||
|
type: "Color",
|
|||
|
uniforms: {
|
|||
|
color: Cesium.Color.RED
|
|||
|
}
|
|||
|
}
|
|||
|
}),
|
|||
|
})
|
|||
|
primitive.appearance = appearance;
|
|||
|
|
|||
|

|
|||
|
|
|||
|
3、接下来我们为外观添加片元着色器,并将shadertoy上的shader赋值给外观的片元着色器属性。
|
|||
|
|
|||
|
shadertoy上glsl代码:
|
|||
|
|
|||
|
fragmentShaderSource: `
|
|||
|
void mainImage( out vec4 fragColor, in vec2 fragCoord )
|
|||
|
{
|
|||
|
vec2 p = (2.0*fragCoord.xy-iResolution.xy)/iResolution.y;
|
|||
|
float tau = 3.1415926535*2.0;
|
|||
|
float a = atan(p.x,p.y);
|
|||
|
float r = length(p)*0.75;
|
|||
|
vec2 uv = vec2(a/tau,r);
|
|||
|
|
|||
|
//get the color
|
|||
|
float xCol = (uv.x - (iTime / 3.0)) * 3.0;
|
|||
|
xCol = mod(xCol, 3.0);
|
|||
|
vec3 horColour = vec3(0.25, 0.25, 0.25);
|
|||
|
|
|||
|
if (xCol < 1.0) {
|
|||
|
|
|||
|
horColour.r += 1.0 - xCol;
|
|||
|
horColour.g += xCol;
|
|||
|
}
|
|||
|
else if (xCol < 2.0) {
|
|||
|
|
|||
|
xCol -= 1.0;
|
|||
|
horColour.g += 1.0 - xCol;
|
|||
|
horColour.b += xCol;
|
|||
|
}
|
|||
|
else {
|
|||
|
|
|||
|
xCol -= 2.0;
|
|||
|
horColour.b += 1.0 - xCol;
|
|||
|
horColour.r += xCol;
|
|||
|
}
|
|||
|
|
|||
|
// draw color beam
|
|||
|
uv = (2.0 * uv) - 1.0;
|
|||
|
float beamWidth = (0.7+0.5*cos(uv.x*10.0*tau*0.15*clamp(floor(5.0 + 10.0*cos(iTime)), 0.0, 10.0))) * abs(1.0 / (30.0 * uv.y));
|
|||
|
vec3 horBeam = vec3(beamWidth);
|
|||
|
fragColor = vec4((( horBeam) * horColour), 1.0);
|
|||
|
}
|
|||
|
`
|
|||
|
|
|||
|
操作步骤:
|
|||
|
a、首先我们需要修改着色器入口函数,即将mainImage修改为main,因为Appearance片元着色器的入口函数是main。
|
|||
|
b、然后将输出结果的fragColor修改为out\_FragColor。
|
|||
|
c、最后我们按照上面说的替换掉fragCoord,fragCoord、iTime
|
|||
|
|
|||
|
我们需要的代码:
|
|||
|
|
|||
|
fragmentShaderSource: `
|
|||
|
void main()
|
|||
|
{
|
|||
|
float iTime=czm_frameNumber/100.;
|
|||
|
vec2 p = (2.0 * gl_FragCoord.xy-czm_viewport.zw)/czm_viewport.w;
|
|||
|
|
|||
|
float tau = 3.1415926535*2.0;
|
|||
|
float a = atan(p.x,p.y);
|
|||
|
float r = length(p)*0.75;
|
|||
|
vec2 uv = vec2(a/tau,r);
|
|||
|
|
|||
|
//get the color
|
|||
|
float xCol = (uv.x - (iTime / 3.0)) * 3.0;
|
|||
|
xCol = mod(xCol, 3.0);
|
|||
|
vec3 horColour = vec3(0.25, 0.25, 0.25);
|
|||
|
|
|||
|
if (xCol < 1.0) {
|
|||
|
|
|||
|
horColour.r += 1.0 - xCol;
|
|||
|
horColour.g += xCol;
|
|||
|
}
|
|||
|
else if (xCol < 2.0) {
|
|||
|
|
|||
|
xCol -= 1.0;
|
|||
|
horColour.g += 1.0 - xCol;
|
|||
|
horColour.b += xCol;
|
|||
|
}
|
|||
|
else {
|
|||
|
|
|||
|
xCol -= 2.0;
|
|||
|
horColour.b += 1.0 - xCol;
|
|||
|
horColour.r += xCol;
|
|||
|
}
|
|||
|
|
|||
|
// draw color beam
|
|||
|
uv = (2.0 * uv) - 1.0;
|
|||
|
float beamWidth = (0.7+0.5*cos(uv.x*10.0*tau*0.15*clamp(floor(5.0 + 10.0*cos(iTime)), 0.0, 10.0))) * abs(1.0 / (30.0 * uv.y));
|
|||
|
vec3 horBeam = vec3(beamWidth);
|
|||
|
out_FragColor = vec4((( horBeam) * horColour), 1.0);
|
|||
|
}
|
|||
|
`
|
|||
|
|
|||
|

|
|||
|
|
|||
|
但是移动地球我们会发现绘制的结果始终是在屏幕中心,这其实是一个正确的结果,因为这个示例在shadertoy上也是始终绘制在屏幕中心。现在我们需要将Primitive的几何体作为绘制的画布,上面我们是使用gl\_FragCoord坐标来获取当前应该处理的像素,我们现在需要改变为Appearance的纹理坐标,通过Appearence的纹理坐标,计算当前Appearence上需要处理的像素。
|
|||
|
|
|||
|
vec2 p = (2.0 * gl_FragCoord.xy-czm_viewport.zw)/czm_viewport.w;
|
|||
|
|
|||
|
该行代码的意思是将绘图区的宽高转换到[-1,1]的一个区间中。而在Appearance的片元着色器中,相对于该Primitive对应的Geometry而言,绘图区的宽高已经被限制在了[0,1]的区间了,这可以由片元着色器的st推断,因为片元着色器的st一般就是[0,1]。现在我们改造一下代码,将绘图区限定到Appearance的纹理区间中
|
|||
|
|
|||
|
vec2 p = 2.0 * v_st - 1.;//(2.0*fragCoord.xy-iResolution.xy)/iResolution.y;
|
|||
|
|
|||
|
因为v\_st区间是[0,1],所以我们的变换一下到[-1,1],运行结果如下
|
|||
|

|
|||
|
|
|||
|
到这里我们已经成功将shadertoy上这个示例的shader移植到Cesium上的Primitve中
|
|||
|
|
|||
|
**四、着色器使用技巧**
|
|||
|
|
|||
|
上面示例的shader成功移植到Cesium中的Primitive上。分享2个在使用shadertoy上的shader时的技巧:
|
|||
|
|
|||
|
1、在Cesium中如何选择对应的载体作为画布
|
|||
|
因为shadertoy上的shader类型canvas的绘制原理,是将canvas作为一个画布,在Cesium中我们可以选择Entity或Primtive来作为载体。又因为要方便操作片元着色器,所以我们选择了Primitive,但是Primitive中又有很多Geometry类型,那具体使用哪种Geometry呢?根据经验,最好选择像RectangleGeometry、PlaneGeomery这种比较规则的几何类型,因为shader绘图其实是根据纹理坐标来实现的,而这种规则的几何往往 它的纹理坐标也比较规则。
|
|||
|
|
|||
|
2、如何去除黑色背景
|
|||
|
虽然我们已经成功将shader移植到Cesium中,但是黑色的背景着实有点丑,我们使用shader的初衷是为了好看、酷炫,这效果好像有点违背了我们的初衷。那我们该如何去除这个黑色的背景呢?我们可以分析一下这个黑色,其实黑色的值就是vec3(0,0,0),rgb三个分量越接近于0,就越黑,当然,这也代表着r+g+b约接近0,所以我们可以这样去消除黑色背景。
|
|||
|
|
|||
|
out_FragColor = vec4(color,color.r+color.g+color.g);
|
|||
|
|
|||
|

|
|||
|
|
|||
|
这样就可以了。
|
|||
|
|
|||
|
**五、难度加深,带有输入纹理数据的着色器示例(应用)**
|
|||
|
|
|||
|
实现一个[shader](https://www.shadertoy.com/view/ldfyzl)上带有输入纹理数据的例子
|
|||
|

|
|||
|
|
|||
|
按照 “在Cesium中如何使用” 一节的思路,我们先创建好Primitive、Appearance,然后将着色器代码拷贝进来并修改相关参数。
|
|||
|
|
|||
|
let xMin = 115.894604, yMin = 39.516896, xMax = 117.431959, yMax = 40.630521;
|
|||
|
const rectangle = new Cesium.RectangleGeometry({
|
|||
|
rectangle: rect,
|
|||
|
height: 10000.0,
|
|||
|
});
|
|||
|
const geometry = Cesium.RectangleGeometry.createGeometry(rectangle);
|
|||
|
viewer.scene.primitives.add(new Cesium.Primitive({
|
|||
|
geometryInstances: new Cesium.GeometryInstance({
|
|||
|
geometry: geometry
|
|||
|
}),
|
|||
|
appearance: new Cesium.MaterialAppearance({
|
|||
|
material:new Cesium.Material({
|
|||
|
fabric: {
|
|||
|
uniforms: {
|
|||
|
image:"./texture.png"
|
|||
|
}
|
|||
|
}
|
|||
|
})
|
|||
|
,
|
|||
|
fragmentShaderSource: `
|
|||
|
in vec3 v_positionEC;
|
|||
|
in vec3 v_normalEC;
|
|||
|
in vec2 v_st;
|
|||
|
|
|||
|
// Maximum number of cells a ripple can cross.
|
|||
|
#define MAX_RADIUS 2
|
|||
|
|
|||
|
// Set to 1 to hash twice. Slower, but less patterns.
|
|||
|
#define DOUBLE_HASH 0
|
|||
|
|
|||
|
// Hash functions shamefully stolen from:
|
|||
|
// https://www.shadertoy.com/view/4djSRW
|
|||
|
#define HASHSCALE1 .1031
|
|||
|
#define HASHSCALE3 vec3(.1031, .1030, .0973)
|
|||
|
|
|||
|
float hash12(vec2 p)
|
|||
|
{
|
|||
|
vec3 p3 = fract(vec3(p.xyx) * HASHSCALE1);
|
|||
|
p3 += dot(p3, p3.yzx + 19.19);
|
|||
|
return fract((p3.x + p3.y) * p3.z);
|
|||
|
}
|
|||
|
|
|||
|
vec2 hash22(vec2 p)
|
|||
|
{
|
|||
|
vec3 p3 = fract(vec3(p.xyx) * HASHSCALE3);
|
|||
|
p3 += dot(p3, p3.yzx+19.19);
|
|||
|
return fract((p3.xx+p3.yz)*p3.zy);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
void main( )
|
|||
|
{
|
|||
|
|
|||
|
//float resolution = 10. * exp2(-3.*iMouse.x/iResolution.x);
|
|||
|
// vec2 uv = fragCoord.xy / iResolution.y * resolution;
|
|||
|
float resolution =20.;
|
|||
|
vec2 uv = v_st * resolution;
|
|||
|
vec2 p0 = floor(uv);
|
|||
|
|
|||
|
float iTime=czm_frameNumber/100.;
|
|||
|
|
|||
|
vec2 circles = vec2(0.);
|
|||
|
for (int j = -MAX_RADIUS; j <= MAX_RADIUS; ++j)
|
|||
|
{
|
|||
|
for (int i = -MAX_RADIUS; i <= MAX_RADIUS; ++i)
|
|||
|
{
|
|||
|
vec2 pi = p0 + vec2(i, j);
|
|||
|
#if DOUBLE_HASH
|
|||
|
vec2 hsh = hash22(pi);
|
|||
|
#else
|
|||
|
vec2 hsh = pi;
|
|||
|
#endif
|
|||
|
vec2 p = pi + hash22(hsh);
|
|||
|
|
|||
|
float t = fract(0.3*iTime + hash12(hsh));
|
|||
|
vec2 v = p - uv;
|
|||
|
float d = length(v) - (float(MAX_RADIUS) + 1.)*t;
|
|||
|
|
|||
|
float h = 1e-3;
|
|||
|
float d1 = d - h;
|
|||
|
float d2 = d + h;
|
|||
|
float p1 = sin(31.*d1) * smoothstep(-0.6, -0.3, d1) * smoothstep(0., -0.3, d1);
|
|||
|
float p2 = sin(31.*d2) * smoothstep(-0.6, -0.3, d2) * smoothstep(0., -0.3, d2);
|
|||
|
circles += 0.5 * normalize(v) * ((p2 - p1) / (2. * h) * (1. - t) * (1. - t));
|
|||
|
}
|
|||
|
}
|
|||
|
circles /= float((MAX_RADIUS*2+1)*(MAX_RADIUS*2+1));
|
|||
|
|
|||
|
float intensity = mix(0.01, 0.15, smoothstep(0.1, 0.6, abs(fract(0.05*iTime + 0.5)*2.-1.)));
|
|||
|
vec3 n = vec3(circles, sqrt(1. - dot(circles, circles)));
|
|||
|
vec3 color = texture(image_0, uv/resolution - intensity*n.xy).rgb + 5.*pow(clamp(dot(n, normalize(vec3(1., 0.7, 0.5))), 0., 1.), 6.);
|
|||
|
out_FragColor = vec4(color, 1.0);
|
|||
|
}
|
|||
|
`
|
|||
|
})
|
|||
|
}));
|
|||
|
|
|||
|
分析代码:
|
|||
|
|
|||
|
float resolution = 10. * exp2(-3.*iMouse.x/iResolution.x);
|
|||
|
vec2 uv = fragCoord.xy / iResolution.y * resolution;
|
|||
|
|
|||
|
意思是将uv转换到[0,x]的区间,在Cesium中应该这样写,我们先固定一个值
|
|||
|
|
|||
|
float resolution =20.;
|
|||
|
vec2 uv = v_st * resolution;
|
|||
|
|
|||
|
本节的重点是
|
|||
|
|
|||
|
vec3 color = texture(iChannel0, uv/resolution - intensity*n.xy).rgb + 5.*pow(clamp(dot(n,normalize(vec3(1., 0.7, 0.5))), 0., 1.), 6.);
|
|||
|
texture(iChannel0, uv/resolution - intensity*n.xy).rgb
|
|||
|
|
|||
|
从纹理中采样颜色值,这个iChannel0是sampler2D,在Cesium对应Cesium.Texture,不过我们可以直接传一张图片,Cesium会自动为我们封装成Cesium.Texture。将图片传递到着色器,这里我们采用Materail的属性uniforms,需要注意的是如果您在Appearance的片元着色器中使用Material中的uniforms参数值时,你必须在参数名的后面加上一个序号,比如你的第一个参数为在Material的uniforms中为image,那在Appearance的片元着色器中必须使用image\_0,如下:
|
|||
|
|
|||
|
texture(image_0, uv/resolution - intensity*n.xy).rgb
|
|||
|
|
|||
|

|
|||
|
|
|||
|
**六、Perlin-Worley噪声实现云图**
|
|||
|
|
|||
|
shadertoy示例地址:https://www.shadertoy.com/view/3dVXDc
|
|||
|
|
|||
|

|
|||
|
|
|||
|
该示例使用了Perlin+Worley(柏林+沃利)噪声来实现云的效果,并且是先将结果绘制到一张Texture中,然后在主函数中根据纹理坐标读取纹理中的值。
|
|||
|
1、首先是噪声函数,定义在Common中
|
|||
|

|
|||
|
|
|||
|
2、然后是绘制到Texture中,过程在BufferA中
|
|||
|

|
|||
|
|
|||
|
3、最后在主函数中调用
|
|||
|

|
|||
|
|
|||
|
4、主函数中为了显示了各个噪声的结果,将画布拆分为了5份,我们可以注释掉一些不需要的结果显示,比如修改代码如下:
|
|||
|
|
|||
|
void mainImage( out vec4 fragColor, in vec2 fragCoord )
|
|||
|
{
|
|||
|
vec2 st = fragCoord / iResolution.xy;
|
|||
|
vec2 uv = fragCoord / iResolution.y;
|
|||
|
st.x *= 5.; // 5 columns for different noises
|
|||
|
uv -= .02 * iTime;
|
|||
|
|
|||
|
vec3 col = vec3(0.);
|
|||
|
|
|||
|
float perlinWorley = textureLod(iChannel0, uv * .5, 0.).x;
|
|||
|
|
|||
|
// worley fbms with different frequencies
|
|||
|
vec3 worley = textureLod(iChannel0, uv, 0.).yzw;
|
|||
|
float wfbm = worley.x * .625 +
|
|||
|
worley.y * .125 +
|
|||
|
worley.z * .25;
|
|||
|
|
|||
|
// cloud shape modeled after the GPU Pro 7 chapter
|
|||
|
float cloud = remap(perlinWorley, wfbm - 1., 1., 0., 1.);
|
|||
|
cloud = remap(cloud, .85, 1., 0., 1.); // fake cloud coverage
|
|||
|
|
|||
|
// if (st.x < 1.)
|
|||
|
// col += perlinWorley;
|
|||
|
// else if(st.x < 2.)
|
|||
|
// col += worley.x;
|
|||
|
// else if(st.x < 3.)
|
|||
|
// col += worley.y;
|
|||
|
// else if(st.x < 4.)
|
|||
|
// col += worley.z;
|
|||
|
// else if(st.x < 5.)
|
|||
|
col += cloud;
|
|||
|
|
|||
|
// column dividers
|
|||
|
// float div = smoothstep(.01, 0., abs(st.x - 1.));
|
|||
|
// div += smoothstep(.01, 0., abs(st.x - 2.));
|
|||
|
// div += smoothstep(.01, 0., abs(st.x - 3.));
|
|||
|
// div += smoothstep(.01, 0., abs(st.x - 4.));
|
|||
|
|
|||
|
// col = mix(col, vec3(0., 0., .866), div);
|
|||
|
|
|||
|
fragColor = vec4(col,1.0);
|
|||
|
}
|
|||
|
因为我们不需要绘制到Texture的步骤,所以需要合并BufferA和主函数的代码
|
|||
|
uv -= .02 * iTime;
|
|||
|
vec2 m = vec2(0.5);
|
|||
|
|
|||
|
vec4 col = vec4(0.);
|
|||
|
|
|||
|
float slices = 128.; // number of layers of the 3d texture
|
|||
|
float freq = 4.;
|
|||
|
|
|||
|
float pfbm= mix(1., perlinfbm(vec3(uv, floor(m.y*slices)/slices), 4., 7), .5);
|
|||
|
pfbm = abs(pfbm * 2. - 1.); // billowy perlin noise
|
|||
|
|
|||
|
col.g += worleyFbm(vec3(uv, floor(m.y*slices)/slices), freq);
|
|||
|
col.b += worleyFbm(vec3(uv, floor(m.y*slices)/slices), freq*2.);
|
|||
|
col.a += worleyFbm(vec3(uv, floor(m.y*slices)/slices), freq*4.);
|
|||
|
col.r += remap(pfbm, 0., 1., col.g, 1.); // perlin-worley
|
|||
|
|
|||
|
float perlinWorley = col.r;
|
|||
|
|
|||
|
// worley fbms with different frequencies
|
|||
|
vec3 worley = col.yzw;
|
|||
|
float wfbm = worley.x * .625 +
|
|||
|
worley.y * .125 +
|
|||
|
worley.z * .25;
|
|||
|
|
|||
|
// cloud shape modeled after the GPU Pro 7 chapter
|
|||
|
float cloud = remap(perlinWorley, wfbm - 1., 1., 0., 1.);
|
|||
|
cloud = remap(cloud, .65, 1., 0., 1.); // fake cloud coverage
|
|||
|
|
|||
|
vec3 col_ = vec3(0.);
|
|||
|
col_ += cloud;
|
|||
|
|
|||
|
5、在cesium中如何实现:我们先创建好Primitive、Appearance,然后将着色器代码拷贝进来并修改相关参数
|
|||
|
|
|||
|
let xMin = 115.894604, yMin = 39.516896, xMax = 117.431959, yMax = 40.630521;
|
|||
|
const rectangle = new Cesium.RectangleGeometry({
|
|||
|
rectangle: rect,
|
|||
|
height: 10000.0,
|
|||
|
});
|
|||
|
const geometry = Cesium.RectangleGeometry.createGeometry(rectangle);
|
|||
|
viewer.scene.primitives.add(new Cesium.Primitive({
|
|||
|
geometryInstances: new Cesium.GeometryInstance({
|
|||
|
geometry: geometry
|
|||
|
}),
|
|||
|
appearance: new Cesium.MaterialAppearance({
|
|||
|
material:new Cesium.Material({
|
|||
|
fabric: {
|
|||
|
uniforms: {
|
|||
|
image:"./texture.png"
|
|||
|
}
|
|||
|
}
|
|||
|
})
|
|||
|
,
|
|||
|
fragmentShaderSource: `
|
|||
|
in vec3 v_positionEC;//顶点 相机(眼睛)坐标系
|
|||
|
in vec3 v_normalEC;//顶点法线 相机(眼睛)坐标系
|
|||
|
in vec2 v_st;//纹理坐标 uv
|
|||
|
/**
|
|||
|
This tab contains all the necessary noise functions required to model a cloud shape.
|
|||
|
*/
|
|||
|
|
|||
|
// Hash by David_Hoskins
|
|||
|
#define UI0 1597334673U
|
|||
|
#define UI1 3812015801U
|
|||
|
#define UI2 uvec2(UI0, UI1)
|
|||
|
#define UI3 uvec3(UI0, UI1, 2798796415U)
|
|||
|
#define UIF (1.0 / float(0xffffffffU))
|
|||
|
|
|||
|
vec3 hash33(vec3 p)
|
|||
|
{
|
|||
|
uvec3 q = uvec3(ivec3(p)) * UI3;
|
|||
|
q = (q.x ^ q.y ^ q.z)*UI3;
|
|||
|
return -1. + 2. * vec3(q) * UIF;
|
|||
|
}
|
|||
|
|
|||
|
float remap(float x, float a, float b, float c, float d)
|
|||
|
{
|
|||
|
return (((x - a) / (b - a)) * (d - c)) + c;
|
|||
|
}
|
|||
|
|
|||
|
// Gradient noise by iq (modified to be tileable)
|
|||
|
float gradientNoise(vec3 x, float freq)
|
|||
|
{
|
|||
|
// grid
|
|||
|
vec3 p = floor(x);
|
|||
|
vec3 w = fract(x);
|
|||
|
|
|||
|
// quintic interpolant
|
|||
|
vec3 u = w * w * w * (w * (w * 6. - 15.) + 10.);
|
|||
|
|
|||
|
// gradients
|
|||
|
vec3 ga = hash33(mod(p + vec3(0., 0., 0.), freq));
|
|||
|
vec3 gb = hash33(mod(p + vec3(1., 0., 0.), freq));
|
|||
|
vec3 gc = hash33(mod(p + vec3(0., 1., 0.), freq));
|
|||
|
vec3 gd = hash33(mod(p + vec3(1., 1., 0.), freq));
|
|||
|
vec3 ge = hash33(mod(p + vec3(0., 0., 1.), freq));
|
|||
|
vec3 gf = hash33(mod(p + vec3(1., 0., 1.), freq));
|
|||
|
vec3 gg = hash33(mod(p + vec3(0., 1., 1.), freq));
|
|||
|
vec3 gh = hash33(mod(p + vec3(1., 1., 1.), freq));
|
|||
|
|
|||
|
// projections
|
|||
|
float va = dot(ga, w - vec3(0., 0., 0.));
|
|||
|
float vb = dot(gb, w - vec3(1., 0., 0.));
|
|||
|
float vc = dot(gc, w - vec3(0., 1., 0.));
|
|||
|
float vd = dot(gd, w - vec3(1., 1., 0.));
|
|||
|
float ve = dot(ge, w - vec3(0., 0., 1.));
|
|||
|
float vf = dot(gf, w - vec3(1., 0., 1.));
|
|||
|
float vg = dot(gg, w - vec3(0., 1., 1.));
|
|||
|
float vh = dot(gh, w - vec3(1., 1., 1.));
|
|||
|
|
|||
|
// interpolation
|
|||
|
return va +
|
|||
|
u.x * (vb - va) +
|
|||
|
u.y * (vc - va) +
|
|||
|
u.z * (ve - va) +
|
|||
|
u.x * u.y * (va - vb - vc + vd) +
|
|||
|
u.y * u.z * (va - vc - ve + vg) +
|
|||
|
u.z * u.x * (va - vb - ve + vf) +
|
|||
|
u.x * u.y * u.z * (-va + vb + vc - vd + ve - vf - vg + vh);
|
|||
|
}
|
|||
|
|
|||
|
// Tileable 3D worley noise
|
|||
|
float worleyNoise(vec3 uv, float freq)
|
|||
|
{
|
|||
|
vec3 id = floor(uv);
|
|||
|
vec3 p = fract(uv);
|
|||
|
|
|||
|
float minDist = 10000.;
|
|||
|
for (float x = -1.; x <= 1.; ++x)
|
|||
|
{
|
|||
|
for(float y = -1.; y <= 1.; ++y)
|
|||
|
{
|
|||
|
for(float z = -1.; z <= 1.; ++z)
|
|||
|
{
|
|||
|
vec3 offset = vec3(x, y, z);
|
|||
|
vec3 h = hash33(mod(id + offset, vec3(freq))) * .5 + .5;
|
|||
|
h += offset;
|
|||
|
vec3 d = p - h;
|
|||
|
minDist = min(minDist, dot(d, d));
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// inverted worley noise
|
|||
|
return 1. - minDist;
|
|||
|
}
|
|||
|
|
|||
|
// Fbm for Perlin noise based on iq's blog
|
|||
|
float perlinfbm(vec3 p, float freq, int octaves)
|
|||
|
{
|
|||
|
float G = exp2(-.85);
|
|||
|
float amp = 1.;
|
|||
|
float noise = 0.;
|
|||
|
for (int i = 0; i < octaves; ++i)
|
|||
|
{
|
|||
|
noise += amp * gradientNoise(p * freq, freq);
|
|||
|
freq *= 2.;
|
|||
|
amp *= G;
|
|||
|
}
|
|||
|
|
|||
|
return noise;
|
|||
|
}
|
|||
|
|
|||
|
// Tileable Worley fbm inspired by Andrew Schneider's Real-Time Volumetric Cloudscapes
|
|||
|
// chapter in GPU Pro 7.
|
|||
|
float worleyFbm(vec3 p, float freq)
|
|||
|
{
|
|||
|
return worleyNoise(p*freq, freq) * .625 +
|
|||
|
worleyNoise(p*freq*2., freq*2.) * .25 +
|
|||
|
worleyNoise(p*freq*4., freq*4.) * .125;
|
|||
|
}
|
|||
|
|
|||
|
void main( )
|
|||
|
{
|
|||
|
vec2 uv = v_st;
|
|||
|
float iTime=czm_frameNumber/100.;
|
|||
|
uv -= .02 * iTime;
|
|||
|
vec2 m = vec2(0.5);
|
|||
|
|
|||
|
vec4 col = vec4(0.);
|
|||
|
|
|||
|
float slices = 128.; // number of layers of the 3d texture
|
|||
|
float freq = 4.;
|
|||
|
|
|||
|
float pfbm= mix(1., perlinfbm(vec3(uv, floor(m.y*slices)/slices), 4., 7), .5);
|
|||
|
pfbm = abs(pfbm * 2. - 1.); // billowy perlin noise
|
|||
|
|
|||
|
col.g += worleyFbm(vec3(uv, floor(m.y*slices)/slices), freq);
|
|||
|
col.b += worleyFbm(vec3(uv, floor(m.y*slices)/slices), freq*2.);
|
|||
|
col.a += worleyFbm(vec3(uv, floor(m.y*slices)/slices), freq*4.);
|
|||
|
col.r += remap(pfbm, 0., 1., col.g, 1.); // perlin-worley
|
|||
|
|
|||
|
float perlinWorley = col.r;
|
|||
|
|
|||
|
// worley fbms with different frequencies
|
|||
|
vec3 worley = col.yzw;
|
|||
|
float wfbm = worley.x * .625 +
|
|||
|
worley.y * .125 +
|
|||
|
worley.z * .25;
|
|||
|
|
|||
|
// cloud shape modeled after the GPU Pro 7 chapter
|
|||
|
float cloud = remap(perlinWorley, wfbm - 1., 1., 0., 1.);
|
|||
|
cloud = remap(cloud, .65, 1., 0., 1.); // fake cloud coverage
|
|||
|
|
|||
|
vec3 col_ = vec3(0.);
|
|||
|
col_ += cloud;
|
|||
|
|
|||
|
out_FragColor = vec4(col_,col.r); `
|
|||
|
})
|
|||
|
}));
|
|||
|
|
|||
|
这样就实现了体积云类似效果
|
|||
|

|
|||
|
|
|||
|
**七、流体水面**
|
|||
|
示例地址:https://www.shadertoy.com/view/4dd3Rl
|
|||
|

|
|||
|
|
|||
|
取到代码在cesium中如何使用参考上面体积云的示例,步骤相同。
|
|||
|
|
|||
|
<link href="https://csdnimg.cn/release/blogv2/dist/mdeditor/css/editerView/markdown_views-f23dff6052.css" rel="stylesheet">
|
|||
|
<link href="https://csdnimg.cn/release/blogv2/dist/mdeditor/css/style-e504d6a974.css" rel="stylesheet">
|