Code |
||
Friday, 3 November, 2006, 08:45 PM Just to let you know, I like to try my best to describe things in as simple a way as possible. I often need to use scary words to describe what I`m doing but I try my best to keep them to a minimum. If you are looking for a "Reference Manual" style explanation of anything, don`t bother looking here. thanks :) Fog Shader Version 2 ---------------------- Whilst my original fog shader works and seems to me to make logical sense, I have been shown a new and improved way to do fog with GLSL. Thanks to Matthias Mann I have implemented fog with all the same characteristics of my previous example, but without the need to send the shader the current Camera Position. This version of fog will also work with more diferent styles of game/demo. The idea behind this is simply that the vertex shader takes a vertex and calculates it`s position on screen. It then calculates the distance on the z axis and interpolates fog itself. The only limitation I have found with this, is that it doesnt work properly with VERY LARGE polygons (so far, 256*512 is too big for it to work properly). and now the code: Vertex Shader varying float Distance; void main() { gl_TexCoord[0] = gl_MultiTexCoord0; gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; Distance = clamp(gl_Position.z,0.0,300.0); } Fragment Shader varying float Distance; uniform sampler2D TheTexture; void main (void) { vec4 color= texture2D(TheTexture, gl_TexCoord[0].st); gl_FragColor = mix(color, vec4(0.5,0.5,0.5,1.0), Distance/300.0); } FOG with GLSL (OpenGL Shading Language) Before I begin with the details of how to make a fog shader with GLSL, I will go over the basics of making ANY shader. The following code might well be specific to LWJGL, but I am sure that anyone reading this will be wise enough to be able to convert it into their language of choice. Firstly, we need to realise that a GLSL Shader is made up of 2 separate files, a vertex shader and a fragment shader.In simple terms, the vertex shader is called for every individual vertex (each vertex has an x,y and z coordinate and I think of them as pixels,hmmmm what can I say) you throw at it and a fragment shader works with lumps of pixels(vertices - normally a quad or a triangle). You might be thinking, "Oh!, I only want it to work on whole quads, not each pixel, so I`ll leave out the vertex shader.". Nope, sorry, you can`t do that. But you can make a vertex shader which basicly just throws the pixels on to the fragment shader unchanged. ok, so we know the basics of what is needed. Lets take a look at the basic vertex shader Basic vertex shader void main() { gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; } This does nothing except take in the pixels and send them onto the fragment shader unchanged (in reality, it projects them into the correct positions within your ModelViewProjectionMatrix, but pah!). Take a look at the format of the commands with the void main() and the curly brackets, these things are the basic layout and requirements of all shaders. you will see them in every example. Now onto the fragment shader. The way I see it, we need 2 separate basic fragment shaders. One for textured fragments and one for coloured fragments. So here goes. Textured fragment: uniform sampler2D TheTexture; void main (void) { gl_FragColor = texture2D(TheTexture, gl_TexCoord[0].st); } Coloured fragment: void main (void) { gl_FragColor=vec4(1.0,0.0,0.0, 1.0);//red } The first thing to notice is the similarities between these two (we will look at the diferences afterwards). Both comply with the same basic layout as the vertex shader and both end with setting the gl_FragColor. The colour fragment sets the FragColor to a 4 item vector representing RGBA (red,green,blue and Alpa). The Texture fragment setis it to the texture. Which texture? Well, now we come to the diferences between the 2 fragments. The first line of the texture fragment is "uniform sampler2D TheTexture;" what we have here is 3 elements, "uniform" which tells the shader that a variable is coming in from your application. "sampler2D" which is the class of the variable and "TheTexture" which is the name I have chosen to give to this variable. There are many diferent types of uniform variables that shaders can accept and I honestly think at this point, I would be wasting your time and mine to explain them all. Lets stick with what we have, a sampler2D,which just means a 2D texture. The big question now is how does our application communicate with our shaders, and this was the part which I personally struggled most with. Until, that is, I found a little Class written by Abdul Bezrati which wraps it all up for me. His little Class (after I fixed the memory leaks, Phew!) has been a godsend and I will link to it here. This means that we can set up our shaders and send our variables with vary little code: import GLShaders; //our shader wrapper public int basicShader; //an integer reference to our shader basicShader = GLShaders.loadShadersCode("basic.vert","basic.frag"); myTexture = TextureLoader.get().getTexture("anyTextureIWant.tga"); GL20.glUseProgram(basicShader); //start using this shader GL13.glActiveTexture(GL13.GL_TEXTURE0); GL11.glBindTexture(GL11.GL_TEXTURE_2D, myTexture.getTextureID()); GLShaders.sendUniform1i(basicShader, "myTexture",0); 1)Firstly, we have to import our GLSL wrapper 2)This creates a variable to reference the shader we are making 3)This little piece of code will take the 2 files we have saved and compile a completed ready to use shader 4) Now we have to load in the texture we want to use (I use a wrapper for this too). 5)start using the shader 6)set the active texture unit to number one 7)bind our texture to the active texture unit 8)now simply send an integer uniform variable to our shader which is our texture. now we have everything we need to make and use our basic shader, I can finally start talking about the fog shader. Fog vertex shader uniform vec3 CameraPosition;// the current camera position (x,y,z) varying float Distance;// the distance we will send forward void main() { vec4 a = gl_Vertex; float dx = a.x - CameraPosition.x; float dy = a.y - CameraPosition.y; float dz = a.z - CameraPosition.z; Distance = clamp((dx*dx)+(dy*dy)+(dz*dz),0.0,50000.0); gl_TexCoord[0] = gl_MultiTexCoord0; gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; } well, if this needs explaining at all, we need to clamp the distance variable so that for objects very far away we dont increase the grey colour beyond the max. Fog fragment shader varying float Distance; uniform sampler2D TheTexture; void main (void) { vec4 color= texture2D(TheTexture, gl_TexCoord[0].st); gl_FragColor = mix(color, vec4(0.5, 0.5, 0.5, 1), Distance/50000.0); } that`s it really, calculate the distance from camera to the pixel in the vertex shader, allow the fragment shader to interpolate the values itself and mix the resulting values with the colour grey :) BILINEAR INTERPOLATION ---------------------- "What the hell is bilinear interpolation?" I hear you ask. Let me make a few assumptions here to make my explanation simpler. You have made a heightmap Terrain and you want to "walk" around your terrain. You realise that you can set your height at any point on your heightmap and sail around on or just above your terrain. But wait, you realised that when your terrain changes height, you "jump" from the height at one point to the height at the next. then you start wondering what to do about smoothly sliding from one height to the next and you finally come to the conclusion that you need some kind of algorithm for working it out for you. Am I about right so far? ok, so this is where interpolation comes in to the picture. Interpolating is the process of "working out the bits in between". there are loads of algorithms to do this for you, and I have tried most of them with varying degrees of success. I finally settled on bilinear interpolation as it is quite simply the best one out there without getting stupidly complex. Next, how do we calculate this algorithm? well I went and found out the details from my favourite info site (en.wikipedia.org) and from there decided to look a little further. I found a lovely site that gave me everything I needed here and coded the little thing in. it looks really complicated on there, so here`s what I boiled it down to: private float InterpolateGridHeight(float xpos,float zpos,int STEP_SIZE){ float h00 = getheight((int)xpos,(int)zpos); float h10 = getheight((int)xpos+STEP_SIZE,(int)zpos); float h01 = getheight((int)xpos,(int)zpos+STEP_SIZE); float h11 = getheight((int)xpos+STEP_SIZE,(int)zpos+STEP_SIZE); float h1=h00; float h2=h10; float h3=h01; float h4=h11; float a00 = h1; float a10 = h2-h1; float a01=h3-h1; float a11=h1-h2-h3+h4; float partialx=xpos-(int)xpos; float partialz =zpos-(int)zpos; float hi=a00+(a10*partialx)+(a01*partialz)+(a11*partialx*partialz); return hi; } so all we do is send our x and z positions(float) along with the step size - normally 1. And this little baby will interpolate the correct height for us and return it ready to be used as our y position to sit us comfortably on the terrain. simple :) RANDOM HILLS IN HEIGHTMAP ------------------------- RANDOM HILLS IN HEIGHTMAP ------------------------- This is the code I use to generate random hills within my heightmap:- int hillcentreX=100; int hillcentreY=100; int hillradius=5; for (int myiterator=0;myiterator<20;myiterator+= STEP_SIZE){ hillcentreX=(MAP_SIZE/4)+(int)(Math.random()*(MAP_SIZE/2)); hillcentreY=(MAP_SIZE/4)+(int)(Math.random()*(MAP_SIZE/2)); hillradius=1+(int)(Math.random()*MAP_SIZE/10); for ( int thisX = 0; thisX < MAP_SIZE; thisX += STEP_SIZE ){ for ( int thisY = 0; thisY < MAP_SIZE; thisY += STEP_SIZE ){ float newheight= (((hillradius*hillradius)((thisX-hillcentreX)*(thisX-hillcentreX)) +((thisY-hillcentreY)*(thisY-hillcentreY))))/hillradius ); if (newheight>0 )addheight(heightmap,thisX,thisY,newheight); } } } private void addheight(float[] b, int x, int y,float newvalue){ x %= MAP_SIZE; y %= MAP_SIZE; b[x + (y * MAP_SIZE)]+=newvalue ; } Hmmmm. I think I`ll have to improve that at some point. |
||







