Metall-Material

Hallo
Ich frage mich, wie ich den Folgenden “Metall-Effekt”(Wie bei diesem Wasserhahn) in OpenGL hinbekommen kann, bzw. was ich dafür alles brauche.

Als Textur für Eisen habe ich mal die von hier genommen.
Mir ist klar, dass man hier vor allem mit Licht arbeiten muss, diffuses und Specular-Licht habe ich schon implementiert(Shininess: 0.5).
Was brauche ich an Licht sonst noch, bzw. muss ich den Shininess-Koeffizienten ändern?
Ist die Textur in Ordnung, oder brauch ich eine andere?
Gibt es sonst noch was dazu zu wissen?

Falls du das als Einstieg noch nicht selbst gefunden hast: https://de.wikipedia.org/wiki/Environment_Mapping ist hier das Zauberwort.

Kennst du ein gutes Tutorial zu diesem Thema? Ich hab mich da mal reingelesen, konnte aber kein Tutorial finden, aus dem ich wirklich schlau wurde.

Kein konkretes. Wenn ich vieeeel Zeit hätte, würde ich mal schauen, sowas wie https://github.com/McNopper/OpenGL/tree/master/Example08 zum Testen nach Java zu portieren. Vielleicht weiß @Fancy not was genaueres.

Danke, dieses Beispiel ist gut. Aber eins versteh ich nicht:


glusImageLoadTga("cm_pos_x.tga", &image);
    glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, image.format, image.width, image.height, 0, image.format, GL_UNSIGNED_BYTE, image.data);
    glusImageDestroyTga(&image);

Warum wird hier ein Bild geladen? Ich dachte die Textur besteht aus dem, was “gespiegelt” wird. Und wie sieht die cm_pos_x.tga dann aus?

Das bild ist ein Bild einer (virtuellen) Umgebung, wie z.B. die Beispiele auf https://en.wikipedia.org/wiki/Cube_mapping

ECHTE Spiegelungen kriegt man nur vernünftig mit einem Raytracer hin. In den Objekten wird sich also nichts spiegeln außer der “Umgebung”, die durch die Environment Map vorgegeben ist.

Kann man nicht Mithilfe eines Framebuffers die Umgebung rendern und dann als Environmentmap verwenden?

Meine das bei einigen Spielen gesehen zu haben, die Spiegel oder spiegelndes Glas verwenden.
Sieht halt hässlich aus, weil die Umgebung nur alle paar Sekunden aktualisiert wird was man eindeutig merkt… bei verworrenem Matall ist das sowieso nicht so wichtig.

Achso, jetzt versteh ich das. Aber wenn ich die Welt nicht richtig reflektiere, wie erstelle ich denn nun am besten eine Cubemap, die den obigen Effekt hat?

Ich bin nicht ganz sicher, ob ich verstehe, was du meinst. Eine Cubemap für die im ersten Beitrag gezeigte “Welt” wäre wohl eine, wo alle Wände und die Decke komplett weiß sind, und nur entlang der vertikalen Kanten schwarze Streifen sind - wenn das dann in einem solchen Zylinder reflektiert wird, würde es etwa so aussehen, wie der Wasserhahn.

Zum Ersten: Ich habe mal meinen Fragmentshader um das, was im Beispiel steht ergänzt, aber er sagt “cannot call a non-function”, und texture2D funktioniert nicht mit vec3.


vec3 normal = normalize(transpose(inverse(mat3(modelMat))) * fragNormal);
	reflectView = reflect(vec3(frag_Vert*modelMat), normal);
	v_reflect = reflectView*invert(viewMat);
	if(textured == 1){
		if(cubeMapEnabled == 1){
			sc = texture(texture, v_reflect);
		}else{
			sc = texture2D(texture, TexCoord);
		}
	}

Zum zweiten: Kann man diese Cubemap auch generieren, ich hätte gerne eine, die für jedes beliebige Objekt gut aussieht.

Hast du die Textur auch als “samplerCube” (und nicht nur als “sampler”) im shader?

Wie oben bereits gesagt, könntest du einen Framebuffer verwenden um deine Cubemap zu generieren indem du nach dem Aufbau deiner Umgebung einfach die 6x mal in alle Würfelseitenrichtungen einmal in einen Framebuffer renderst.

Das würde natürlich keine Veränderungen der Umgebung mit einschließen die danach passieren würden.
Ansonsten müsstest du das jeden Frame machen, aber kannst dir ja selber ausrechnen wenn du bereits für eine Cubemap 6x mehr Rendern musst wie sich das auf deine FPS auswirken wird. (Tipp: Durch 6 teilen)
Und dann fragen sich die Nutzer

„Warum ist die Performance so scheiße und warum hat nach 5 Jahren Entwicklung das Spiel eigentlich immernoch keinen wirklich spielbaren Inhalt?“
und dann musst du darauf antworten:
„Ist alles für die realistisch glitzernde Dose da draufgegangen die dort am Boden liegt und die Umgebung völlig verschwommen und unkenntlich spiegelt ihr aber nie bemerkt habt oder auch nur je den Effekt überhaupt gesehen hättet.“
→ ??? → Money

Ne Ernsthaft die Environmentmap nur alle paar Frames neu zu rendern würde schon gehen, aber dynamisches Rendering von Spiegelungen aber auch Lichtquellen oder Schatten, ist alles ganz nett anzusehen aber zieht auch eine Menge Entwicklungszeit und Performance.

Also das mit dem samplerCube hab ich jetzt. Es muss ja nicht “Mega Gut” aussehen, ich möchte nur, dass das Metall ein bisschen realistischer aussieht und nicht nur…grau:

Ihr werdet sicher einsehen, dass das echt mies aussieht!

dann hau doch ne “realistischere” textur drauf?.. wo ist das problem…

*** Edit ***

achso und das “einfach nur graue” ding würde mit antialiasing tausend mal besser aussehen ^^

Ja, wie sieht denn die Environment Map in diesem Fall aus? Allgemein wird sich bei einer perfekt planen Fläche mit einfarbigen Enwironment Maps nicht so viel tun, aber… sieht man wenigstens, dass es keine „normale graue Textur“ ist, wenn man die Ansicht dreht?
@TMII Man kann unterschiedliche Prioritäten setzen. Schau mal, manche schreiben sogar Programme, die nichts anderes machen als ein einfaches Objet mit einer Environment Map anzuzeigen (und das ist auch gut so - im Zweifelsfall hat man dann einen Schnipsel, den man selbst (oder, wenn man ihn auf Github stellt, sogar jemand anderes) leicht in ein Spiel einbauen kann :slight_smile: ) Abgesehen davon: FALLS bei dem, was im Screenshot zu sehen ist, eine reduzierung der Framerate um den Faktor 6 dafür sorgen würde, dass weniger als 100 FPS gerendert werden, wäre sowieso was falsch gelaufen :wink:

Das Antialiasing werde ich mir mal angucken, danke für den Tipp!
Ich hab mal einen eigenen Shader für das ganze geschrieben, und zeichne dann das “Gemappte” damit.Es wird nichts angezeigt, aber die Zeichenmethode wird ausgeführt. Wahrscheinlich habe ich nur wieder irgendwas übersehen. Und bevor sich wieder jemand über den Code aufregt, der wird noch abgeändert, zum Beispiel das mit den Locations, ich hab den nur schnell runtergetippt.

activity.mappingShader.use();
		glUniform1i(activity.mappingShader.getUniformLocation("u_cubemapTexture"), 0);
		glActiveTexture(GL_TEXTURE0);
	    glBindTexture(GL_TEXTURE_2D, cubeMap.id);
	    
	    glUniformMatrix4(activity.mappingShader.getUniformLocation("projection"), false, activity.projMatrix);
	    glUniformMatrix4(activity.mappingShader.getUniformLocation("view"), false, activity.viewMatrix);
	    glUniformMatrix4(activity.mappingShader.getUniformLocation("model"), false, activity.modelMatrix);
	    
		glBindVertexArray(vaoID);
        glEnableVertexAttribArray(0);
        glEnableVertexAttribArray(1);
        
        glDrawElements(mode, indexBuffer);
        
        glDisableVertexAttribArray(0);
        glDisableVertexAttribArray(1);
        glBindVertexArray(0);

#version 140
#extension GL_ARB_explicit_attrib_location : require

uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;

mat3 normalMatrix;

layout (location = 0) in vec3 in_Position;
layout (location = 1) in vec3 in_Normal;

out vec3 v_reflect;

void main()
{
	normalMatrix = transpose(inverse(mat3(model)));

	vec4 vertex = view * model * vec4(in_Position, 0);
	
	vec3 normal = normalMatrix * in_Normal;
	
	vec3 incident = vec3(vertex); 

	vec3 reflectView = reflect(incident, normal);
	
	v_reflect = mat3(inverse(view)) * reflectView;

	gl_Position = projection * vertex;
}


#version 140

uniform samplerCube u_cubemapTexture;

in vec3 v_reflect;

out vec4 fragColor;

void main()
{
	fragColor = texture(u_cubemapTexture, v_reflect);
}

Auch habe ich den Inhalt der Matrizen überprüft, die sind alle “gut gefüllt”, daran kanns also nicht liegen.Auch die ganzen IDs der Textur und des VAOs sind belegt. Falls irgendwem der Fehler direkt auffällt, gerne schreiben. Wie gesagt, wahrscheinlich irgend ein “Leichtsinnsfehler”.

Du, weiß, wie es läuft: @Fancy yyyyy! :smiley:

Moin,

[QUOTE=Zombiepriester]

	    glBindTexture(GL_TEXTURE_2D, cubeMap.id);
```[/QUOTE]

das sieht zumindest schon falsch aus, es sollte wohl GL_TEXTURE_CUBE_MAP sein. Wie sieht den die Methode zum laden der Bilder aus? (GL_TEXTURE_CUBE_MAP_POSITIVE_X bis GL_TEXTURE_CUBE_MAP_NEGATIVE_Z?).

Kurzes googeln brachte [das hier als ganz brauchbares Tutorial](http://antongerdelan.net/opengl/cubemaps.html) hervor. Imho solltest Du sowohl den SkyBox Teil aus auch den Reflecting Teil lesen, da diese aufeinander aufbauen.

Ansonsten würde ich immer (egal bei was) empfehlen erst mal ein eigenes KSKB zu bauen. Damit stellt man einfach sicher das man das Thema verstanden hat und hat außerdem eine funktionierende Lösung. Etwas direkt in ein umfangreicheres Programm zu integrieren schafft einfach zusätzliche Fehlerquellen.

Gruß
Fancy

Habe jetzt eine CubeMap Klasse erstellt. Ich habe nach dem Tutorial auch alles richtig gemacht(denke ich zumindest), aber es wird nichts angezeigt. Ich glaube nicht, dass es an der Cubemap liegt, denn wenn ich das mache, wird auch nichts angezeigt:


#version 140

uniform samplerCube u_cubemapTexture;

in vec3 v_reflect;

out vec4 fragColor;

void main()
{
	fragColor = vec4(0, 0, 0, 1);//texture(u_cubemapTexture, v_reflect);
}

Trotzdem mal die CubeMap-Klasse für das Bild:

import java.awt.image.BufferedImage;
import java.io.IOException;
import java.nio.ByteBuffer;

import javax.imageio.ImageIO;

import org.lwjgl.opengl.GL11;

import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL13.*;
import static org.lwjgl.opengl.GL12.*;

public class CubeMap {

	public int id;
	
	public CubeMap(String path) throws IOException{
		this(ImageIO.read(CubeMap.class.getClassLoader().getResourceAsStream(path)));
	}
	
	public CubeMap(BufferedImage image){
		glActiveTexture(GL_TEXTURE0);
		id = glGenTextures();
		int w = image.getWidth(), h = image.getHeight();
		
		loadSide(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, image.getSubimage(w/4, h/3, w/4, h/3));
		loadSide(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, image.getSubimage(3*w/4, h/3, w/4, h/3));
		loadSide(GL_TEXTURE_CUBE_MAP_POSITIVE_X, image.getSubimage(2*w/4, h/3, w/4, h/3));
		loadSide(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, image.getSubimage(0, h/3, w/4, h/3));
		loadSide(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, image.getSubimage(w/4, 0, w/4, h/3));
		loadSide(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, image.getSubimage(w/4, 2*(h/3), w/4, h/3));
		
		glTexParameteri (GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri (GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameteri (GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
		glTexParameteri (GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
		glTexParameteri (GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	}

	private void loadSide(int target,
			BufferedImage bi) {
		glBindTexture(GL_TEXTURE_CUBE_MAP, id);
		byte[] data = (byte[]) bi.getRaster().getDataElements(0, 0, bi.getWidth(), bi.getHeight(), null);
        ByteBuffer image = ByteBuffer.allocateDirect(bi.getColorModel().getPixelSize() / 8 * bi.getWidth() * bi.getHeight());
        image.put(data);
        image.rewind();
		glTexImage2D(target, 0, GL11.GL_RGBA, bi.getWidth(), bi.getHeight(), 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, image);
	}
}

Das Zeichnen ist gleich geblieben, bis auf das glBindTexture.