Shaders start

hello to all,

i came to an point at my engine where i really need something like
an shader for some graphic effects that i cannot do with the fixed pipe functions.

the jogl/net beans examples with shaders are very very complicated to understand

my goal is simple, a textured quad with an texture and this texture
should be distorted a bit like an chaos…my english is not the best :
when you divide the quad in hundrets of quads and then
make an random on the u/v positions (just a litte bit around the original uv)
so that the texture on the whole quad looks like an chaos (or a bad water)

i now i need vertex/fragment shader and started to try to understand some code i found

the code below did not do anything, the quad is rendered without any effect
and the loadShader Code i found in the forum here without knowing what it is doing,
its simple something to check is anything happen.

i hope there is somebody who can help me

thanks :slight_smile:


  private String[] loadShader ( void ) {

    int t = 5;
    String shader[] = new String[t];

//i dont know that this is doing!

    shader[0]="uniform samplerRect BaseImage;";
    shader[1]="void main(void) {";
    shader[2]="vec4 tmp = textureRect(BaseImage,gl_TexCoord[0].st);";
    shader[3]="tmp += 0.5;";
    shader[4]="gl_FragColor = tmp;";

    return shader;

}


  private void init_shader(GLAutoDrawable drawable) {

        GL gl = drawable.getGL();

         programm = gl.glCreateProgramObjectARB ();
		
         int frag = gl.glCreateShaderObjectARB ( GL.GL_FRAGMENT_SHADER_ARB );

         gl.glAttachObjectARB ( programm, frag );

        gl.glShaderSourceARB ( frag, 1, loadShader , null );

         gl.glCompileShaderARB ( frag );
         gl.glLinkProgramARB ( programm );

         int texImage = gl.glGetUniformLocationARB ( programm, "test" );
         gl.glUseProgramObjectARB ( programm );
         gl.glUniform1iARB ( texImage, 0 );


	}

private void render_quad(GLAutoDrawable drawable) 
{
        GL gl = drawable.getGL();



gl.glUseProgram(program);


 gl.glBegin(GL.GL_QUADS);
      gl.glColor4f(1f,1f,1f,1.0f);
      gl.glVertex3f( -1000,0,-1000);
      gl.glColor4f(fr,fg,fb,1.0f);
      gl.glVertex3f(  1000,0,-1000);
      gl.glColor4f(fr,fg,fb,1.0f);
      gl.glVertex3f(  1000,0, 1000);
      gl.glColor4f(fr,fg,fb,1.0f);
      gl.glVertex3f( -1000,0, 1000);
 gl.glEnd();

gl.glUseProgram(0);

}




There are some minor problems in your code. For starters, your glShaderSourceARB arguments are wrong. You have to give the number of strings in the String[] as second argument and I don’t know if you can use null as fourth argument, since this assumes, that the strings are null-terminated (which java strings afaik aren’t).

Another problem might be samplerRect/textureRect, because they don’t appear in the GLSL standard (you’ll need an NPOT extension).

The next problem is that you are trying to set the uniform variable “test”, which isn’t defined in your shader (it is called BaseImage in shader[0]).

At last, you might need to bind a texture to the desired texture units before rendering your quad.

Here is a working template:


package net.highteq.jogl;

import com.sun.opengl.util.Animator;
import com.sun.opengl.util.texture.Texture;
import com.sun.opengl.util.texture.TextureIO;
import java.awt.Frame;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.media.opengl.GL;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLCanvas;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.glu.GLU;
import static javax.media.opengl.GL.*;

/**
 * SimpleShaderTemplate.java
 * @author Mathias 'cylab' Henze
 */
public class SimpleShaderTemplate implements GLEventListener
{
	private GLU glu = new GLU();

	// Hold the shader program for later use
	private int program;
	// Holds the texture object for later use
	private int textureID;

	public static void main(String[] args)
	{
		Frame frame = new Frame("Simple JOGL Application");
		GLCanvas canvas = new GLCanvas();

		canvas.addGLEventListener(new SimpleShaderTemplate());
		frame.add(canvas);
		frame.setSize(640, 480);
		final Animator animator = new Animator(canvas);
		frame.addWindowListener(new WindowAdapter()
		{
			@Override
			public void windowClosing(WindowEvent e)
			{
				// Run this on another thread than the AWT event queue
				// so that Animator.stop() completes before exiting
				new Thread(new Runnable()
				{
					public void run()
					{
						animator.stop();
						System.exit(0);
					}
				}).start();
			}
		});
		frame.setLocationRelativeTo(null);
		frame.setVisible(true);
		animator.start();
	}

	public void init(GLAutoDrawable drawable)
	{
		// drawable.setGL(new DebugGL(drawable.getGL()));
		GL gl = drawable.getGL();

		// You won't get far without error checking ;)
		String errors = null;

		try
		{
			// Load some texture
			Texture texture = TextureIO.newTexture(SimpleShaderTemplate.class.getResource("texture.png"), false, "png");

			// Store the OpenGL texture object ID for later use
			textureID = texture.getTextureObject();
		}
		catch (Exception ex)
		{
			Logger.getLogger(SimpleShaderTemplate.class.getName()).log(Level.SEVERE, null, ex);
		}

		// This is the shader source. For simplicity just use a single concatenated string
		String[] shader =
		{
			// This variable specifies the texture unit to use, called "sampler" in GLSL
			"uniform sampler2D texUnit;\n" +
			// main() is called for every pixel on screen (called "fragment" in GLSL) while rasterizing
			"void main(void)\n" +
			"{\n" +
			    // The gl_TexCoord array is a build in GLSL variable that holds the glTexCoordXX() values
			"   vec2 texCoord = gl_TexCoord[0].xy;\n" +
			    // texture2D() gets a texel from the texture unit at the given location
			"   vec4 texel = texture2D(texUnit, texCoord);\n" +
			    // Now assign the texel to be the current pixels output color.
			    // Mask out green, to see that the shader is indeed used (vec4 is r,g,b,a)
			"   gl_FragColor = texel * vec4(1.0, 0.0, 1.0, 1.0);\n" +
			"}\n"
		};

		// Since java Strings aren't 0-terminated, store the length for submission to glShaderSource
		// (maybe this unnecessary and JOGL implicitly 0-terminates the Strings, but you never know)
		int[] lengths = { shader[0].length() };

		// Create and store a shader program
		program = gl.glCreateProgramObjectARB();

		// Create the fragment shader object
		int frag = gl.glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);

		// Tell the shader program to use the fragment shader
		gl.glAttachObjectARB(program, frag);

		// Copy the shader source to GL implementation.
		// You need the amount of Strings, the String[] and an array of the Strings lenghts
		gl.glShaderSourceARB(frag, shader.length, shader, lengths, 0);

		// Compile the shader
		gl.glCompileShaderARB(frag);

		// Check for compile errors
		if ((errors = getGLErrorLog(gl, frag)) != null)
			throw new RuntimeException("Compile error\n" + errors);

		// Link the program
		gl.glLinkProgramARB(program);

		// Check for link errors
		if ((errors = getGLErrorLog(gl, program)) != null)
			throw new RuntimeException("Link error\n" + errors);

		// Use the program, to be able to assign values to the uniforms
		gl.glUseProgramObjectARB(program);

		// Get the location of the texUnit uniform, so we can assign a value to it
		int texUnit = gl.glGetUniformLocationARB(program, "texUnit");

		// Assign the GL_TEXTURE0 unit to the uniform variable
		// CAUTION: you need to set the plain texture _unit_ _number_,
		// not the GL constant nor the textur object here!!!
		gl.glUniform1iARB(texUnit, 0);

		// Enable VSync
		gl.setSwapInterval(1);

		// Setup the clear color
		gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

	}

	public void display(GLAutoDrawable drawable)
	{
		GL gl = drawable.getGL();

		// Clear the drawing area
		gl.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		// Reset the current matrix to the "identity"
		gl.glLoadIdentity();

		// Move the "drawing cursor" around
		gl.glTranslatef(0.0f, 0.0f, -3.0f);

		// Choose the current texture unit
		gl.glActiveTexture(GL_TEXTURE0);

		// Bind a texture to the current texture unit
		gl.glBindTexture(GL_TEXTURE_2D, textureID);

		// Enable the shader program
		gl.glUseProgramObjectARB(program);

		// Draw a quad to be textured
		gl.glBegin(GL_QUADS);
		{
			gl.glTexCoord2f(0, 0);
			gl.glVertex3f(-1, -1, 0);
			gl.glTexCoord2f(1, 0);
			gl.glVertex3f(1, -1, 0);
			gl.glTexCoord2f(1, 1);
			gl.glVertex3f(1, 1, 0);
			gl.glTexCoord2f(0, 1);
			gl.glVertex3f(-1, 1, 0);
		}
		gl.glEnd();

		// Disable all shaders
		gl.glUseProgramObjectARB(0);

		// Flush all drawing operations to the graphics card
		gl.glFlush();
	}

	public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height)
	{
		GL gl = drawable.getGL();

		if (height <= 0)
		{ // avoid a divide by zero error!

			height = 1;
		}
		final float h = (float) width / (float) height;
		gl.glViewport(0, 0, width, height);
		gl.glMatrixMode(GL_PROJECTION);
		gl.glLoadIdentity();
		glu.gluPerspective(45.0f, h, 1.0, 20.0);
		gl.glMatrixMode(GL_MODELVIEW);
		gl.glLoadIdentity();
	}

	public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged)
	{
	}

	// Checks for arbitrary GL errors. Could also be accomplished by enabling the DebugGL pipeline
	private String getGLError(GL gl)
	{
		boolean hasError = false;
		String message = "";
		for (int glErr = gl.glGetError(); glErr != GL.GL_NO_ERROR; glErr = gl.glGetError())
		{
			message += (hasError ? "\n" : "") + glu.gluErrorString(glErr);
			hasError = true;
		}
		return hasError ? message : null;
	}

	// Checks the info log for compile/link errors
	private String getGLErrorLog(GL gl, int obj)
	{
		boolean hasError = false;
		int[] infologLength = {0};
		int[] charsWritten = {0};
		byte[] infoLog;

		String message = "";
		String error = getGLError(gl);
		if (error != null)
		{
			message += error;
			hasError = true;
		}

		gl.glGetObjectParameterivARB(obj, GL_OBJECT_INFO_LOG_LENGTH_ARB, infologLength, 0);
		error = getGLError(gl);
		if (error != null)
		{
			message += (hasError ? "\n" : "") + error;
			hasError = true;
		}

		if (infologLength[0] > 1)
		{
			infoLog = new byte[infologLength[0]];
			gl.glGetInfoLogARB(obj, infologLength[0], charsWritten, 0, infoLog, 0);
			message += (hasError ? "\n" : "") + "InfoLog:\n" + new String(infoLog);
			hasError = true;
		}
		error = getGLError(gl);
		if (error != null)
		{
			message += (hasError ? "\n" : "") + error;
			hasError = true;
		}
		return hasError ? message : null;
	}
}

Just put a texture.png beside the java class and off you go.

Edit: forgot to remove my failure-test ::slight_smile:

thank you very very much :slight_smile:

that code works perfect und its a great base to learn
the shader stuff on an living object :wink:

much thanks again :slight_smile:

I think there needs to be a better basic shader tutorial(especially for JOGL). I mean I learned how to use them but, all of the GLSL tutorials I’ve found assume that the reader knows a lot of things that, I’m sure most beginners no NOTHING about.

Obviously shader’s aren’t a basic concept, but there shouldn’t be such a big hurdle holding back potential shader programmers. That’s all opinion, but thanks Cylab for contributing that code. I even picked up a pointer from it :D.

it makes really fun working with the fragment shader :slight_smile:

i got only with water an simple/nice bump mapping water to work :slight_smile:

there is a litte question i am working on:

when using
gl.glEnable( GL.GL_BLEND );
and the fragment shader

the blending is complete deaktivated (without frag shader it works)

gl.glColor4f(1,1,1, 0.4f);
gl.glNormal3f(0,1,0);

what code did i need to use in the frag code
that the alpha values will be used?

greetings :slight_smile:

example:

String[] shader2 =
{
“uniform sampler2D texUnit,texUnit2;\n” +
“uniform float v1,v2;\n” +
“float vi =1.0;\n” +
“float vii=1.0;\n” +
“void main(void)\n” +
“{\n” +
" vec2 texCoord = gl_TexCoord[0].xy;\n" +
" vec4 texel = texture2D(texUnit , texCoord);\n" +
“texCoord[0]=texCoord[0]*5.0;\n”+
"texCoord[1]=texCoord[1]5.0;\n"+
" vec4 texel2 = texture2D(texUnit2 , texCoord);\n" +
" gl_FragColor = texel2;\n" + // texel2
texel
“}\n”
};

When enabling shaders you do more or less deactivate the functionality of the fixed function pipeline, so you have to do it yourself. But this is easy, since the color values in GLSL are RGBA, so you just need to specify an appropriate alpha value in the shader like texel.a=0.3. Blending with the framebuffer is then computed according to the current blend mode. Combining texels from multiple textures is up to you (e.g. premultiply the color components with the alpha and add the color vectors)

If you want to experiment with shaders more, you should load them from the classpath. Netbeans provides a shader file editor with gl function completion that could ease things a bit.

Here is a utility method to add to your code to load and compile a shader from a arbitrary location:


    private int loadAndCompileShader(GL gl, int type, URL location) throws IOException
    {
        BufferedReader reader = null;
        try
        {
            reader = new BufferedReader(new InputStreamReader(location.openStream()));
            ArrayList lineList = new ArrayList(100);
            for (String line = reader.readLine(); line != null; line = reader.readLine())
            {
                lineList.add(line + "\n");
            }
            String[] lines = (String[]) lineList.toArray(new String[lineList.size()]);
            int[] lengths = new int[lines.length];
            for (int i = 0; i < lines.length; i++)
            {
                lengths[i] = lines[i].length();
            }
            int shader = gl.glCreateShader(type);
            gl.glShaderSourceARB(shader, lines.length, lines, lengths, 0);

            gl.glCompileShaderARB(shader);
            String error = getGLErrorLog(gl, shader);

            int[] compileStatus = {0};
            gl.glGetObjectParameterivARB(shader, GL.GL_OBJECT_COMPILE_STATUS_ARB, compileStatus, 0);
            if (compileStatus[0] == 0)
            {
              throw new IllegalArgumentException("Shader could not be compiled! " + (error == null ? "" : error));
            }
            return shader;
        }
        finally
        {
            if (reader != null) try{ reader.close(); } catch(Exception ignoreSunsInsanity){}
        }
    }

You can use it like:


    int program = gl.glCreateProgram();
    gl.glAttachShader(program, loadAndCompileShader(gl, GL_FRAGMENT_SHADER, YourMain.class.getResource("shaders/your-shader.frag"));

to load, compile and attach a shader from your source tree relative to your main class.

thank you very much,

at this time i got an vertex/fragment water done by myself :slight_smile:

there are some litte questions open:

i load via

int to  = gl.glGetUniformLocationARB(program, "tex1");
gl.glUniform1iARB(t0, 0);

//fragment shader...
"uniform sampler2D tex1"

Texture Handles to the fragment shader ,
my question

how can i use and access an array of sampler2d

“uniform sampler2D tex[20]”


gl.glUniform1ivARB is not working and
i didnt find any examples how to copy an
array to the frament shader programm


gl.glGetUniformLocationARB(program, "tex1")
int[] samplers = {/* your 20 values ...*/};
gl.glUniform1ivARB(t0, 20, samplers, 0);

should work, but are you sure, that you need 20 texture units in your shader???

[quote]should work, but are you sure, that you need 20 texture units in your shader???
[/quote]
yes, its an crazy idea… the idea is simple:

its a kind of multitexturing for an ground landscape
without changing the textures all the time…
my first experiments with the vertex/fragment shader working perfect
and it speeds up the whole project like i never seen bevor.
(from 15fps (without shader) up to 150fps(with that shader) on an
notebook with an geforce GO7300 (shared memory)

its unbelievable…

the vertex shader sends the edge information (e1-e4) to the fragment shader
this e1-e4 holds the texture number of the sampler2d…
(thats why i need the sample2d in an array,)
the fragment shader itself calculates the frag_color by a mix of the 4 edges with the texturecoords…

i am very happy with that stuff

i use texture stage(sampler2d) 1-15…that is supported by most of the graphic cards (8,16,32)
14 elements (like gras,stone,sand…etc) and the 15 is the Height/Color Map Texture

later i will add an gras function to the fragment shader that will create
gras on the surface (like an 3d effect without using extra vertices)…i dont use first person view.

I was already guessing, that you want to do realtime texture splatting, but I would have thought 8 units to be sufficient. Let’s see some screenies, once you get finished :slight_smile:

:slight_smile:

my only problem that is did not undertand is the handles of simple variables in the
fragment shader that slows me down,
the combination of uniform(external variables) and vars in the fragment make problems

//java source
int eec1 = gl.glGetUniformLocationARB(program, “eec1”);
int eec2 = gl.glGetUniformLocationARB(program, “eec2”);
int eec3 = gl.glGetUniformLocationARB(program, “eec3”);
int eec4 = gl.glGetUniformLocationARB(program, “eec4”);

    gl.glUniform1iARB(eec1, 0);
    gl.glUniform1iARB(eec2, 1);
    gl.glUniform1iARB(eec3, 2);
    gl.glUniform1iARB(eec4, 3);

//fragment shader

uniform int eec1,eec2,eec3,eec4; //this vars come from the java source (not from vertex shader)

//test example
int ec1=1;
int ec2=1;
int ec3=1;
int ec4=1;


Main

ec1=int (eec1); <-this stops the shader…(simple int+ external int)

vec4 texel_av=0;
texel_av += texture2D(tex[ec1] , texCoord)*pow1;
texel_av += texture2D(tex[ec2] , texCoord)*pow4;
texel_av += texture2D(tex[ec3] , texCoord)*pow2;
texel_av += texture2D(tex[ec4] , texCoord)*pow3;

is there something i did forgot or are there some restrictions
of handling variables in the fragment shader?