Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

A shader about fog

edited January 6 in Shaders Posts: 190

A shader about fog, porting in Java, can control the fog height, suitable for all kinds of mesh terrain.

https://youtu.be/OHtn8e0h1yg

Code is below:

function setup()
    parameter.number("cx",-1000,1000,220)
    parameter.number("cy",-1000,2000,175)
    parameter.number("cz",-1000,1000,437)
    parameter.number("tx",-100,100,-4)
    parameter.number("ty",-100,200,0)
    parameter.number("tz",-100,100,0)
    parameter.number("fov",-360,360,17) 
    parameter.number("slabY",-100,200,17) 

    local yArray = loadLandForms()
    m = Mountain(yArray, #yArray-1, #yArray[1]-1)
end

function draw()
    background(0)    
    m:draw()    
end    

function loadLandForms()
    result = {}
    landHighAdjust = 2;
    landHighest = 60;
    myImg = readImage(asset.land);
    local w,h = myImg.width, myImg.height
    for i=1,h,1 do
        result[i] = {}
        for j=1,w,1 do
            r,g,b,a = myImg:get(j,i)
            local mh = (r+g+b)/3
            result[i][j] = mh*landHighest/255 - landHighAdjust
        end
    end
    return result
end

Mountain = class()

function Mountain:init(yArray, rows, cols)
    self.mesh = mesh()
    self.unitSize = 3
    self.mesh.vertices = {}
    self.mesh.texCoords = {}
    self:initVertexData(yArray, rows, cols)
    self:initShader()
end

function Mountain:initShader()
    self.mesh.shader = shader(vs35.v,vs35.f)
    self.mesh.shader.slabY = slabY or 13
    self.mesh.shader.startAngle = 0
    self.mesh.shader.uCameraLocation = vec3(0,25,0)  
    self.mesh.shader.landStartY = 0
    self.mesh.shader.landYSpan = 50
    self.mesh.shader.modelMatrix = modelMatrix()   
    self.mesh.shader.sTextureRock = readImage(asset.grass)
    self.mesh.shader.sTextureGrass = readImage(asset.LMStone)
end

function Mountain:draw()
    pushMatrix()
    translate(0,0,1)
    camera(cx,cy,cz,tx,ty,tz,0,1,0)
    perspective(fov,WIDTH/HEIGHT,0.01,5000)    
    self.mesh.shader.slabY = slabY
    self.mesh.shader.startAngle = ((self.mesh.shader.startAngle+0.03)%360)
    self.mesh.shader.uCameraLocation = vec3(cx,cy,cz)  
    self.mesh:draw()
    popMatrix()
end

function Mountain:initVertexData(yArray, rows, cols)    
    local count = 0
    local vertices = {}
    local unit = self.unitSize

    for j=1,rows,1 do
        for i=1,cols,1 do
            zsx = -unit*cols/2 + i*unit
            zsz = -unit*rows/2 + j*unit

            vertices[count+1] = vec3(zsx, yArray[j][i], zsz)
            vertices[count+2] = vec3(zsx, yArray[j+1][i], zsz+unit)          
            vertices[count+3] = vec3(zsx+unit, yArray[j][i+1], zsz)
            vertices[count+4] = vec3(zsx+unit, yArray[j][i+1], zsz)            
            vertices[count+5] = vec3(zsx, yArray[j+1][i], zsz+unit)            
            vertices[count+6] = vec3(zsx+unit, yArray[j+1][i+1], zsz+unit) 
            count = count+6           
        end
    end   
    self.mesh.vertices = vertices
    self.mesh.texCoords = self:generateTexcoords(cols, rows) 
end

function Mountain:generateTexcoords(bw, bh)
    local result = {}
    sizeW = 16/bw
    sizeH = 16/bh
    local c =0
    for i=1,bh,1 do
        for j=1,bw,1 do
            local s,t = j*sizeW, i*sizeH

            result[c+1] = vec2(s,t) 
            result[c+2] = vec2(s, t + sizeH)
            result[c+3] = vec2(s + sizeW, t)            
            result[c+4] = vec2(s + sizeW, t)            
            result[c+5] = vec2(s, t + sizeH)            
            result[c+6] = vec2(s + sizeW, t + sizeH)
            c=c+6
        end        
    end
    return result
end

vs35 = {
    v = [[
    #version 300 es
    uniform mat4 modelViewProjection;       //总变换矩阵
    uniform mat4 modelMatrix;       //基本变换矩阵
    uniform mat4 viewMatrix;        //基本变换矩阵    
    in vec3 position;       //顶点位置
    in vec2 texCoord;           //顶点纹理坐标
    out vec2 vTextureCoord;         //用于传递给片元着色器的纹理坐标
    out float currY;                //用于传递给片元着色器的Y坐标
    out vec4 pLocation;             //用于传递给片元着色器的顶点坐标

    void main(){     
        vTextureCoord = texCoord;                   //将接收的纹理坐标传递给片元着色器
        currY = position.y;                     //将顶点的Y坐标传递给片元着色器
        gl_Position=modelViewProjection * vec4(position,1);     //根据总变换矩阵计算此次绘制此顶点的位置
        pLocation= modelMatrix*vec4(position,1);//计算基本变换(平移/旋转/缩放)后顶点的位置
    }     
]],

f = [[
#version 300 es
precision mediump float;                            //给出默认的浮点精度
in vec2 vTextureCoord;                      //接收从顶点着色器传过来的纹理坐标
in float currY;                             //接收从顶点着色器传过来的Y坐标
in vec4 pLocation;                              //接收从顶点着色器传过来的顶点坐标
uniform float slabY;                                //体积雾对应雾平面的高度
uniform float startAngle;                           //扰动起始角
uniform vec3 uCameraLocation;                       //摄像机位置
uniform sampler2D sTextureGrass;                    //纹理内容数据(草皮)
uniform sampler2D sTextureRock;                     //纹理内容数据(岩石)
uniform float landStartY;                           //过程纹理起始Y坐标
uniform float landYSpan;                            //过程纹理跨度
out vec4 fragColor;//输出到的片元颜色

float tjFogCal(vec4 pLocation){//计算体积雾浓度因子的方法

float xAngle=pLocation.x/16.0*3.1415926;//计算出顶点X坐标折算出的角度

float zAngle=pLocation.z/20.0*3.1415926;//计算出顶点Z坐标折算出的角度

float slabYFactor=1.618*sin(xAngle+zAngle+startAngle);//联合起始角计算出角度和的正弦值
//求从摄像机到待处理片元的射线参数方程Pc+(Pp-Pc)t与雾平面交点的t值
float t=(slabY+slabYFactor-uCameraLocation.y)/(pLocation.y-uCameraLocation.y);
//有效的t的范围应该在0~1的范围内,若不存在范围内表示待处理片元不在雾平面以下
if(t>0.0&&t<1.0){//若在有效范围内则
    //求出射线与雾平面的交点坐标
          float xJD=uCameraLocation.x+(pLocation.x-uCameraLocation.x)*t;
          float zJD=uCameraLocation.z+(pLocation.z-uCameraLocation.z)*t;
          vec3 locationJD=vec3(xJD,slabY,zJD);

          float L=distance(locationJD,pLocation.xyz);//求出交点到待处理片元位置的距离
          float L0=10.0;

          return L0/(L+L0);//计算体积雾的雾浓度因子
}else{
    return 1.0;//若待处理片元不在雾平面以下,则此片元不受雾影响
}}
void main(){      

vec4 gColor=texture(sTextureGrass, vTextureCoord);//从草皮纹理中采样出颜色  
vec4 rColor=texture(sTextureRock, vTextureCoord); //从岩石纹理中采样出颜色     



vec4 finalColor;                                    //片元最终颜色
if(currY<landStartY){   

            finalColor=gColor;  //当片元Y坐标小于过程纹理起始Y坐标时采用草皮纹理
}else if(currY>landStartY+landYSpan){

            finalColor=rColor;//当片元Y坐标大于过程纹理起始Y坐标加跨度时采用岩石纹理 
}else{//当片元Y坐标在过程纹理范围内时将草皮和岩石混合

        float currYRatio=(currY-landStartY)/landYSpan;//计算岩石纹理所占的百分比    
        //将岩石、草皮纹理颜色按比例混合
        finalColor= currYRatio*rColor+(1.0- currYRatio)*gColor;
} 
float fogFactor=tjFogCal(pLocation);//计算雾浓度因子
//根据雾浓度因子、雾的颜色及片元本身采样的纹理颜色计算出片元的最终颜色
fragColor=fogFactor*finalColor+ (1.0-fogFactor)*vec4(0.9765,0.97490,0.9549,1.0); //给此片元最终颜色值   
/*
if (fogFactor == 1.0) { fragColor=finalColor;
}else{
    fragColor= fogFactor*finalColor + (1.0-fogFactor)*vec4(0.9765, 0.97490, 0.90549, 1.0); //给此片元最终颜色值   
}
*/

}  

]]
}

Tagged:

Comments

  • Posts: 2,574
    @binaryblues - great demo. Love to see shader examples. Thanks.
  • edited January 6 Posts: 239

    there’s secondary interesting part to this code being the 2D mesh projection into a 3D space and then manipulating the camera() and perspective () calls, i think the roller coaster example does this too, i wonder what kinds of 2.5D effects are possible? can we project a building that shows left/right side more depending on a character position/value ? can the terrain be 3D and another sprite be 2D in that space?

  • Posts: 190

    Thanks to you all.

    @Bri_G I’m glad to see that someone else is as interested in shader as I am, and I’ll share some more of shader’s experimental code.

    @skar In fact, it’s 3D mesh, because its vertex data contains Z coordinates, which is how Codea 3D was implemented before Craft, by explicitly manually adjusting the data for the three matrices(modelMatrix, viewMatrix, projectionMatrix) then multiply them by the vertex coordinates on the GPU, and you end up either using orthogonal projection or perspective projection to bring the object to the screen.

    The functions translate(),rotate() and scale() generate the elements of the modelMatrix; the function camera() generate the elements of the viewMatrix; the functions ortho() and perspective() generate the elements of the projectionMatrix.

    The 2.5D effect you mentioned seems to be set up by ortho(), and by the way, your last question inspired me to release a prototype based on the idea in due course.

Sign In or Register to comment.