Licht in OpenGL

[OT]das mit dem offtopic geht mit OT in eckigen klammern und entsprechendem schließ tag /OT in eckigen klammern.[/OT]

nein, geht es nicht, und da lässt sich eben nicht drüber streiten… das kannst du mir glauben ^^

Das +0 ist OK (mach’ ich normalerweise auch, hat IMHO gelegentlich ganz konkrete Vorteile bzgl. Lesbarkeit, und der JIT kümmert sich drum).

Der Punkt dabei (auch auf den konkreten Fall bezogen) war, dass man bei

			normals.set(ipd.get(d)[0]*3+0, nx);
			normals.set(ipd.get(d)[0]*3+1, ny);
			normals.set(ipd.get(d)[0]*3+2, nz);

erstmal nicht weiß, was da passiert, und man dreimal hin und her überlegen muss, ob es nicht vielleicht doch

			normals.set(ipd.get(d)[0], nx);
			normals.set(ipd.get(d)[1], ny);
			normals.set(ipd.get(d)[2], nz);

oder

			normals.set(ipd.get(d+0)[0], nx);
			normals.set(ipd.get(d+1)[1], ny);
			normals.set(ipd.get(d+2)[2], nz);

oder

			normals.set(ipd.get(d)[0]*3+0, nx);
			normals.set(ipd.get(d)[1]*3+1, ny);
			normals.set(ipd.get(d)[2]*3+2, nz);

heißen müßte. Code sollte sich ähnlich wie normaler Text lesen: „ComputeTriangleNormals(), For(eachVertex) assignTriangleNormal(), CopyVertexNormalsToBuffer()“ … insbesondere, wenn man erwartet, dass andere sich das durchlesen und Fehler darin finden räusper… und nochmal ein bißchen lauter räusper:wink:

Vielleicht schau’ ich morgen nochmal, aber … spätestens wenn’s im Shader liegt, find’ ich das eh nicht durch draufschauen. Hatte nur einmal Teile der FixedFunction-Pipeline nachgebaut, ausgehend von einem Shader, den so ein praktisches Tool mir da generiert hatte, und Tutorials (Directional Light per Pixel & Co)

Am Shader liegts normalerweise nicht, das ist jetzt der, den mymaksimus freundlicherweise gepostet hat, und der bei ihm funktioniert.

So ich habe jetzt den Normalen-Berechnung Code veranschaulicht, mit tollen Variablennamen und Marco13s Alternativen getestet, aber dadurch ist es nur schlimmer geworden.


public FloatBuffer calcNormals(){
		ArrayList<Float> normals = new ArrayList<Float>(); 
		for(int i = 0; i < vertexBuffer.capacity(); i++){
			normals.add(0.0f);
		}
		ArrayList<int[]> indexeprodreieck = new ArrayList<int[]>();
		for(int i = 0; i < indexBuffer.capacity(); i += 3){
			int[] ind = {
					indexBuffer.get(i),indexBuffer.get(i+1),indexBuffer.get(i+2)
			};
			indexeprodreieck.add(ind);
		}
		for(int d = 0; d < indexeprodreieck.size(); d++){
			int[] DreieckIndexPunkt1 = {
					indexeprodreieck.get(d)[0]*3, indexeprodreieck.get(d)[0]*3+1, indexeprodreieck.get(d)[0]*3+2
			};
			int[] DreieckIndexPunkt2 = {
					indexeprodreieck.get(d)[1]*3, indexeprodreieck.get(d)[1]*3+1, indexeprodreieck.get(d)[1]*3+2
			};
			int[] DreieckIndexPunkt3 = {
					indexeprodreieck.get(d)[2]*3, indexeprodreieck.get(d)[2]*3+1, indexeprodreieck.get(d)[2]*3+2
			};
			
			float nx = 0, ny = 0, nz = 0;
			ArrayList<Vector3f> coord_vectors = new ArrayList<Vector3f>();
			for(int i = 0; i < vertexBuffer.capacity(); i += 3){
				coord_vectors.add(new Vector3f(vertexBuffer.get(i), vertexBuffer.get(i+1), vertexBuffer.get(i+2)));
			}
			Vector3f U = new Vector3f();
			Vector3f.sub(coord_vectors.get(indexeprodreieck.get(d)[1]), coord_vectors.get(indexeprodreieck.get(d)[0]), U);
			Vector3f V = new Vector3f();
			Vector3f.sub(coord_vectors.get(indexeprodreieck.get(d)[2]), coord_vectors.get(indexeprodreieck.get(d)[0]), V);
			
			nx = U.y*V.z-U.z*V.y;
			ny = U.z*V.x-U.x*V.z;
			nz = U.x*V.y-U.y*V.x;
			
			normals.set(DreieckIndexPunkt1[0], nx);
			normals.set(DreieckIndexPunkt1[1], ny);
			normals.set(DreieckIndexPunkt1[2], nz);
			
			normals.set(DreieckIndexPunkt2[0], nx);
			normals.set(DreieckIndexPunkt2[1], ny);
			normals.set(DreieckIndexPunkt2[2], nz);
			
			normals.set(DreieckIndexPunkt3[0], nx);
			normals.set(DreieckIndexPunkt3[1], ny);
			normals.set(DreieckIndexPunkt3[2], nz);
			
		}
		float[] ns = new float[normals.size()];
		for(int i = 0; i < ns.length; i++){
			ns** = normals.get(i).floatValue();
		}
		return Main.putFloatCoords(ns);
	}

[OT]nein, dadurch ist es schon etwas besser - ich werde es mir gleich mal ansehen. aber du kannst dir zum beispiel auch angewöhnen, variablen namen immer im lowerCamelCase zu schrieben…[/OT]

*** Edit ***

könntest du auch mal die indicies / verticies posten, die bei dir “nicht funktionieren” ?

*** Edit ***

also das hier läuft wie es soll. Hab deinen Code genommen, variablen namen geändert, und meiner meinung nach unnötige sachen rausgenommen.


import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;

import org.lwjgl.BufferUtils;
import org.lwjgl.util.vector.Vector3f;

public class NormalTest {
	
	public ArrayList<Vector3f> calcNormals(FloatBuffer vertexBuffer, IntBuffer indexBuffer){
		ArrayList<Vector3f> normals = new ArrayList<Vector3f>(indexBuffer.capacity());
		for(int i = 0; i < indexBuffer.capacity(); i+=3){
			Vector3f currentThreeVerticies[] = new Vector3f[3];
			for(int j = 0; j < 3; j++){
				int index = indexBuffer.get(i + j);
				currentThreeVerticies[j] = extractVertex(vertexBuffer, index);
			}
			normals.add(calculateNormal(currentThreeVerticies[0], currentThreeVerticies[1], currentThreeVerticies[2]));
		}
		return normals;
	}
	
	private Vector3f extractVertex(FloatBuffer verticies, int position){
		return new Vector3f(verticies.get(position * 3 + 0), verticies.get(position * 3 + 1), verticies.get(position * 3 + 2));
	}
	
	private Vector3f calculateNormal(Vector3f p1, Vector3f p2, Vector3f p3){
		Vector3f U = new Vector3f();
		Vector3f V = new Vector3f();
		Vector3f.sub(p2, p1, U);
		Vector3f.sub(p3, p1, V);
		return new Vector3f(U.y*V.z-U.z*V.y, U.z*V.x-U.x*V.z, U.x*V.y-U.y*V.x);
	}
	
	public static void main(String[] args){
		float verticies[] = new float[]{
				0.0f, 0.0f, 0.0f,	//0 front left  bottom
				1.0f, 0.0f, 0.0f,	//1 front right bottom
				1.0f, 1.0f, 0.0f,	//2 front right top
				0.0f, 1.0f, 0.0f,	//3 front left  top
				0.0f, 0.0f, 1.0f, 	//4 back  left  bottom
				1.0f, 0.0f, 1.0f,	//5 back  right bottom
				1.0f, 1.0f, 1.0f,	//6 back  right top
				0.0f, 1.0f, 1.0f	//7 back  left  top
		};
		int indicies[] = new int[]{
				0, 3, 1,  1, 3, 2, //front
				4, 5, 7,  5, 6, 7, //back
				4, 7, 0,  0, 7, 3, //left
				5, 1, 6,  1, 2, 5, //right
				3, 7, 6,  3, 6, 2, //top
				0, 1, 4,  1, 5, 4  //bottom
		};
		FloatBuffer vertexBuffer = BufferUtils.createFloatBuffer(verticies.length);
		IntBuffer indexBuffer = BufferUtils.createIntBuffer(indicies.length);
		vertexBuffer.put(verticies);
		indexBuffer.put(indicies);
		new NormalTest().calcNormals(vertexBuffer, indexBuffer).forEach((v) -> {System.out.println(v);});
	}
}```

ich schätze du hast einfach in deinem index buffer flächen ab und zu mal gegen den uhrzeigersinn beschrieben. Dann zeigen
die Normalen in die andere Richtung, das ist dann natürlich falsch. 

Wenn du in meinen indicies[] die richtung auf CCW (counter clock wise) änderst, also im prinzip in einem 3er block die zweite und dritte zahl vertauschst,
werden die normalen auf einmal "in den würfel" zeigen und nicht mehr nach draußen.

Ok daran liegt’s wahrscheinlich, aber ich habe auch ein Werkzeug eingebaut, mit dem man Ingame eigene Formen erstellen kann, indem man immer 3 Punkte verbindet und so beliebig viele Flächen erstellt, die dann ein Objekt bilden. Wie stelle ich sicher, dass da die Indecies in der richtigen Reihenfolge sind?

hmm das ist natürlich eine gute frage.
Vielleicht weiß da ein erfahrener mehr zu, aber was mir spontan einfallen würde:
Du zeigst dem Nutzer die Normale an und machst nen Button oder so um sie umzukehren (im falle dessen das sie falsch ist).
Also das solltest du in jedem Fall machen.

Um das ganze zur verbessern könnte man, was mir gerade einfällt, anhand ein paar kriterien “erraten”, welche normale eine Fläche hat,
zum Beispiel in dem man irgendwie die Winkel, die beide möglichen Normalen mit einer nachbarfläche einschließen, vergleicht oder so…

aber das ist wirklich eine spannende Sache, wie man soetwas macht. Ich werde mal gucken ob sich da was findet

Nuja, üblicherweise muss der Nutzer die Normale sowieso parametrisieren, da sich nur in trivialen Fällen aus einem Dreieck eine sinnvolle Normale bestimmen lässt. Schon bei einer simplen glatten Kugel steht die normale senkrecht zur Kugeloberfläche, aber eben nicht senkrecht zum Dreieck. Der Nutzer muss also nicht nur entscheiden zu welcher Seite die Normale zeigen soll, sondern auch wie die Normale die Oberfläche widerspiegeln soll. Beim modellieren wird häufig ein hoch aufgelöstes Mesh zur Generierung der Normalen genutzt. Die Normalen werden dann als Normal Map exportiert und in OpenGL bei der Beleuchtung per Pixel berücksichtigt - dann werden keine Normalen als Vertex-Attribute benötigt.

Gruß
Fancy

Gut, damit wäre das Problem ja gelöst. Falls sich noch Fragen ergeben sollten, melde ich mich wieder. Ansonsten: Danke an alle, die hier geholfen haben.