Probleme mit Splat-Mapping

Started by Dumblecore, September 07, 2013, 02:58:27 PM

Previous topic - Next topic

Dumblecore

Ich habe heute versucht via Splat-Mapping mehrere Texturen auf ein Terrain Objekt zu malen. Da ich mit Shadern allerdings wenig Erfahrung habe (eigentlich mehr oder weniger gar keine), habe ich versucht das Normal-Mapping Beispiel aus dem Wiki zu modifizieren. Das ganze hat (wie zu erwarten) nicht wirklich geklappt. Mein Ziel ist es eigentlich die Texturen so ähnlich zu malen, wie in Egons RPG, aber irgendwie werden nur alle Texturen ineinander "gematscht". Rufe ich den Shader richtig auf? Und was macht denn eigentlich die Zeile: "ARBShaderObjects.glUniform1iARB(int, int)?

Hier mein Ergebnis:


Das ist der Code, den ich verwendet habe:

Aufruf des Shaders:
TextureManager tm = TextureManager.getInstance();
tm.addTexture("grass" , new Texture("data/textures/grass.png"));
tm.addTexture("sand", new Texture("data/textures/sand.png"));
tm.addTexture("stone", new Texture("data/texture/stone.png"));
tm.addTexture("alpha", new Texture("data/texture/SplatMap.png"));

TextureInfo ti = new TextureInfo(TextureManager.getInstance().getTextureID("grass"));
ti.add(TextureManager.getInstance().getTextureID("sand"), TextureInfo.MODE_MODULATE);
ti.add(TextureManager.getInstance().getTextureID("stone"), TextureInfo.MODE_MODULATE);
ti.add(TextureManager.getInstance().getTextureID("alpha"), TextureInfo.MODE_MODULATE);

buffer = new FrameBuffer(1280, 720, FrameBuffer.SAMPLINGMODE_NORMAL);
buffer.disableRenderer(IRenderer.RENDERER_SOFTWARE);
buffer.enableRenderer(IRenderer.RENDERER_OPENGL, IRenderer.MODE_OPENGL);
buffer.setPaintListener(this);

world = new World();
world.setAmbientLight(200, 200, 200);

Object3D ground = Loader.load3DS(new FileInputStream("data/world/Terrain.3ds"), 20f)[0];
//retexture(ground, "grass", 160); //TODO right tile scale
ground.setTexture(ti);
ground.rotateX(-(float)Math.PI/2f);
world.addObject(ground);
ground.build();
ground.compileAndStrip();
ground.setRenderHook(new SplatMapShader());

Camera cam = world.getCamera();
cam.moveCamera(Camera.CAMERA_MOVEOUT, 150);
cam.moveCamera(Camera.CAMERA_MOVEUP, 170);
cam.lookAt(new SimpleVector());


Der Shader selbst
public class SplatMapShader implements IRenderHook{

private String fragSource = Loader.loadTextFile("data/misc/fragmentshader.glsl");
private String vertexSource = Loader.loadTextFile("data/misc/vertexshader.glsl");
private int prg = 0;
private int fragShade = 0;
private int vertShade = 0;
private boolean init = false;

private int alpha = 0;
private int grass = 0;
private int sand = 0;
private int stone = 0;

@Override
public void afterRendering(int polyID) {
ARBShaderObjects.glUseProgramObjectARB(0);
}

@Override
public void beforeRendering(int polyID) {
if(!init){
init();
}
ARBShaderObjects.glUseProgramObjectARB(prg);
//Was machen diese Zeilen?
ARBShaderObjects.glUniform1iARB(alpha, 1);//<--
ARBShaderObjects.glUniform1iARB(grass, 1);//<--
ARBShaderObjects.glUniform1iARB(sand, 1);//<--
ARBShaderObjects.glUniform1iARB(stone, 1);//<--

}

private void init() {
//Initializing program
prg=ARBShaderObjects.glCreateProgramObjectARB();

//initializing fragment shader
fragShade=ARBShaderObjects.glCreateShaderObjectARB(ARBFragmentShader.GL_FRAGMENT_SHADER_ARB);
//loading & compiling fragment shader from source
byte[] src=fragSource.getBytes();
ByteBuffer shader = BufferUtils.createByteBuffer(src.length);
shader.put(src);
shader.flip();

ARBShaderObjects.glShaderSourceARB(fragShade, shader);

ARBShaderObjects.glCompileShaderARB(fragShade);
ARBShaderObjects.glAttachObjectARB(prg, fragShade);

//repeating stuff for vertex shader
vertShade=ARBShaderObjects.glCreateShaderObjectARB(ARBVertexShader.GL_VERTEX_SHADER_ARB);

src=vertexSource.getBytes();
ByteBuffer shader2 = BufferUtils.createByteBuffer(src.length);
shader2.put(src);
shader2.flip();

ARBShaderObjects.glShaderSourceARB(vertShade, shader2);

ARBShaderObjects.glCompileShaderARB(vertShade);
ARBShaderObjects.glAttachObjectARB(prg, vertShade);

//linking program
ARBShaderObjects.glLinkProgramARB(prg);

Logger.log("Shader compiled!", Logger.MESSAGE);

alpha = getLocation("Alpha");
grass = getLocation("Grass");
sand = getLocation("Sand");
stone = getLocation("Stone");
System.out.println(alpha + "," + grass + "," + sand + ","+ stone);

init = true;
}

private int getLocation(String n) {
byte[] nb=n.getBytes();
ByteBuffer name = BufferUtils.createByteBuffer(nb.length+1);
name.put(nb);
name.put((byte)0);
name.flip();
return ARBShaderObjects.glGetUniformLocationARB(prg, name);
}



@Override
public void onDispose() {
ARBShaderObjects.glDeleteObjectARB(fragShade);
ARBShaderObjects.glDeleteObjectARB(vertShade);
ARBShaderObjects.glDeleteObjectARB(prg);
}

@Override
public boolean repeatRendering() {
return false;
}


Hier der fragment-shader:
uniform sampler2D Alpha;
uniform sampler2D Grass;
uniform sampler2D Stone;
uniform sampler2D Sand;

varying vec4 texCoord;

void main(void)
{
   vec4 alpha   = texture2D( Alpha, texCoord.xy );
   vec4 tex0    = texture2D( Grass, texCoord.xy * 4.0 ); // Tile
   vec4 tex1    = texture2D( Sand,  texCoord.xy * 4.0 ); // Tile
   vec4 tex2    = texture2D( Stone, texCoord.xy * 4.0 ); // Tile

   tex0 *= alpha.r; // Red channel
   tex1 = mix( tex0, tex1, alpha.g ); // Green channel
   vec4 outColor = mix( tex1, tex2, alpha.b ); // Blue channel
   
   gl_FragColor = outColor;
}


und hier noch der vertex-shader:
varying vec4 VertexPosition;

void main() {

    gl_TexCoord[0] = gl_MultiTexCoord0;
    gl_Position = ftransform();
    //VertexPosition = gl_ModelViewMatrix * gl_Vertex;;
    VertexPosition = gl_Vertex;
}

EgonOlsen

Zum einen würde ich empfehlen, die GLSLShader-Klasse zu verwenden anstatt GL direkt anzusprechen. Das Wiki enthält beide Wege, aber die GLSLShader-Klasse ist besser, weil sie mehr Fehlerfälle abfängt und explizite Setter Uniforms usw bietet.

Was deinen Shader angeht...ich bin nicht sicher, ob deine Interpolation so korrekt ist. Aber zumindest sollte es nicht so einfarbig aussehen. Was aber definitiv falsch ist, ist der Teil nach "Was machen diese Zeilen?"...damit setzt du die Sampler auf die entsprechenden Einheiten. Bei dir setzt der Code aber alle auf Unit 1. Das müsste eher so aussehen:


ARBShaderObjects.glUniform1iARB(alpha, 0);//<--
ARBShaderObjects.glUniform1iARB(grass, 1);//<--
ARBShaderObjects.glUniform1iARB(sand, 2);//<--
ARBShaderObjects.glUniform1iARB(stone, 3);//<--


...oder je nachdem, auf welcher Stage du welche Textur aufgebracht hast.

Dumblecore

Super danke für die Antwort :D
Die GLSLShader-Klasse geht wirklich viel einfacher^^
Die Units hab ich jetzt auch richtig gesetzt und am Fragment-Shader noch etwas rumgespielt

Wen die Shader vllt noch jemand braucht:

Fragment:
uniform sampler2D Grass;
uniform sampler2D Sand;
uniform sampler2D Stone;
uniform sampler2D Alpha;

varying vec4 texCoord;

void main()
{

    vec4 texel0 = texture2D(Grass, gl_TexCoord[0].xy * 4.0).rgba;//Faktor kann man nach belieben ändern
    vec4 texel1 = texture2D(Sand, gl_TexCoord[0].xy*4.0).rgba;
    vec4 texel2 = texture2D(Stone, gl_TexCoord[0].xy*4.0).rgba;
    vec4 mixmapTexel = texture2D(Alpha, gl_TexCoord[0].xy).rgba;

    texel0 *= mixmapTexel.r;
    texel1 = mix(texel0,  texel1, mixmapTexel.g);
    vec4 outColor = mix(texel1, texel2, mixmapTexel.b);
   
    gl_FragColor = outColor;
}


Vertex:
varying vec4 VertexPosition;

void main() {

    gl_TexCoord[0] = gl_MultiTexCoord0;
    gl_Position = ftransform();
    //VertexPosition = gl_ModelViewMatrix * gl_Vertex;
    VertexPosition = gl_Vertex;
}


So und hier nochmal das Ergebnis (nicht besonders schön, aber war ja auch nur ein Test):