A shader about fog

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); //?????????   
}
*/

}  

]]
}

@binaryblues - great demo. Love to see shader examples. Thanks.

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?

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.