• Herzlich Willkommen!

    Nach der Schließung von inDiablo.de wurden die Inhalte und eure Accounts in dieses Forum konvertiert. Ihr könnt euch hier mit eurem alten Account weiterhin einloggen, müsst euch dafür allerdings über die "Passwort vergessen" Funktion ein neues Passwort setzen lassen.

    Solltet ihr keinen Zugriff mehr auf die mit eurem Account verknüpfte Emailadresse haben, so könnt ihr euch unter Angabe eures Accountnamens, eurer alten Emailadresse sowie eurer gewünschten neuen Emailadresse an einen Administrator wenden.

[Exotisch] Shader kombinieren

Brokenmind

Diablo-Veteran
Registriert
20 Mai 2007
Beiträge
1.510
:hy: :hy:

Ja, mir ist klar, dass ich das auch in einem "Fach-Forum" hätte posten können, aber da dieses Problem doch sehr speziell ist, dachte ich, das altbekannte Forum tuts auch. Außerdem wisst Ihr doch ohnehin viel mehr ;)

Problem: openGL (c++)
im Phong-Shader werden keine Texturen abgebildet,
im Texture-Shader sieht alles wie ein großes Puzzle aus.

Lösung wäre, beide Shader irgendwie miteinander zu kombinieren. Aber wie :confused:

Phong-Fragment:
Code:
// Phong fragment shader

varying vec3 normal;
varying vec4 position;

void calculateLighting( int light, vec3 N, vec3 V3 )
{
	vec3 VP;
	if ( gl_LightSource[light].position.w == 0.0 )
	{
		VP = normalize( gl_LightSource[light].position.xyz );
	}
	else
	{
		VP = normalize( vec3( gl_LightSource[light].position / gl_LightSource[light].position.w ) - V3 );
	}
	
	vec3 R = normalize( -reflect( VP, N ) );
	vec3 E = normalize( -V3 );
	
	float VPdotNf = dot( VP, N );
	float VPdotNb = -VPdotNf;
	
	VPdotNf = max( VPdotNf, 0.0 );
	VPdotNb = max( VPdotNb, 0.0 );
	
	float ff, fb;
	if (VPdotNf == 0.0)
	{
		ff = 0.0;
	}
	else
	{
		ff = pow( max( dot( R, E ), 0.0 ), gl_FrontMaterial.shininess * 0.25 );
	}
	
	if (VPdotNb == 0.0)
	{
		fb = 0.0;
	}
	else
	{
		fb = pow( max( dot( R, E ), 0.0 ), gl_BackMaterial.shininess * 0.25 );
	}
		
				
	if (gl_FrontFacing)
	{

		gl_FragColor += gl_FrontMaterial.ambient * gl_LightSource[light].ambient +
		                gl_FrontMaterial.diffuse * gl_LightSource[light].diffuse * VPdotNf +
		                ff * gl_FrontMaterial.specular * gl_LightSource[light].specular;	                       
	}
	else
	{

		gl_FragColor += gl_BackMaterial.ambient * gl_LightSource[light].ambient +
		                gl_BackMaterial.diffuse * gl_LightSource[light].diffuse * VPdotNb +
		                fb * gl_BackMaterial.specular * gl_LightSource[light].specular;	                       
	}
}


void main()
{
	vec4 V;
	vec3 N, V3;

	
	V = position;
	if ( V.w != 0.0 )
	{
		V3 = vec3( V ) / V.w;
	}
	else
	{
		V3 = vec3( V );
	}

	N = normalize( normal );
	

	if ( gl_FrontFacing )
	{
		gl_FragColor = gl_FrontMaterial.emission +
			           gl_FrontMaterial.ambient * gl_LightModel.ambient;
	}
	else
	{
		gl_FragColor = gl_BackMaterial.emission +
			           gl_BackMaterial.ambient * gl_LightModel.ambient;	
	}
	

	
	//calculateLighting( 0, N, V3 );
	//calculateLighting( 1, N, V3 );
	calculateLighting( 2, N, V3 );
	calculateLighting( 3, N, V3 );
	//calculateLighting( 4, N, V3 );


	gl_FragColor.a = 1.0;
}

Phong-Vertex:
Code:
// Shared vertex shader

varying vec3 normal;
varying vec4 position;

void main()
{
	normal = normalize( gl_NormalMatrix * gl_Normal );
	position = gl_ModelViewMatrix * gl_Vertex;

	gl_Position = ftransform();
}

Texture-Fragment:
Code:
varying vec3 lightDir,normal;
uniform sampler2D tex;

void main()
{
	vec3 ct,cf;
	vec4 texel;
	float intensity,at,af;
	intensity = max(dot(lightDir,normalize(normal)),0.0);

	cf = intensity * (gl_FrontMaterial.diffuse).rgb +
				  gl_FrontMaterial.ambient.rgb;
	af = gl_FrontMaterial.diffuse.a;
	texel = texture2D(tex,gl_TexCoord[0].st);

	ct = texel.rgb;
	at = texel.a;
	gl_FragColor = vec4(ct * cf, at * af);

}

Texture-Vertex:
Code:
varying vec3 lightDir,normal;

void main()
{
	normal = normalize(gl_NormalMatrix * gl_Normal);

	lightDir = normalize(vec3(gl_LightSource[0].position));
	gl_TexCoord[0] = gl_MultiTexCoord0;

	gl_Position = ftransform();
}

in Google gefunden hab ich zwar das hier, aber das ist wohl alles Javascript; außerdem blick ich generell durch die Shader Language nicht durch.

Also, wie immer: Falls es jemanden gibt, der das Problem kennt oder der wen kennt der wen kennt der das Problem kennt (und es zu lösen vermag) :ugly:, bitte ich ihn/sie, es mir zu erklären :D


PS: Der Texture-Shader holt sich immer die erste Textur auf dem Stack, egal ob sie aktiviert oder deaktiviert ist... kann man diesen Textur-Hunger irgendwie zügeln?


Greetz
Brokenmind
 
Auch wenn dein verlinktes Beispiel JS ist, könnte man es doch für die Shader-Codes (welche Sprache benutzt du da?) zu Rate ziehen. Aber du könntest dir auch einfach Beispiele in C++ suchen :)

Das Beispiel benutzt wohl zum "kombinieren" der Shader O3D, den Teil kannst du also nicht davon übernehmen.

Wenn du OpenGL benutzt (welche Version?), tust du die Shader normalerweise in ein glProgram, das du dann später ein/ausschalten kannst.

Ich habe für's Laden der Shader und einige andere Features ein SDK von nvidia genutzt. Dann könnte das Laden und "Kombinieren" etwa so aussehen:
(OpenGL 2.0, glsl)
Code:
GLuint vertexShader;
GLuint fragmentShader;
GLuint program;

vertexShader = nv::CompileGLSLShaderFromFile( GL_VERTEX_SHADER, "regular_vertex.glsl" );
if ( vertexShader == 0 ) cout << "Could not load vertex shader" << endl;

fragmentShader = nv::CompileGLSLShaderFromFile( GL_FRAGMENT_SHADER, "regular_fragment.glsl" );
if ( fragmentShader == 0 ) cout << "Could not load fragment shader" << endl;

program = glCreateProgram();
glAttachShader( program, vertexShader );
glAttachShader( program, fragmentShader );
glLinkProgram( program );

// check if program linked successfully
GLint success = 0;
glGetProgramiv( program, GL_LINK_STATUS, &success );

if ( !success )
{
	char buf[256];
	glGetProgramInfoLog( program, 256, NULL, buf );
	cout << "Failed to link Program: " << buf << endl;
	glDeleteProgram( program );
	program = 0;
}

Vor dem Rendern der Polygone, die durch die Shader gehen sollen, dann
Code:
glUseProgram( program );

PS: Der Texture-Shader holt sich immer die erste Textur auf dem Stack, egal ob sie aktiviert oder deaktiviert ist... kann man diesen Textur-Hunger irgendwie zügeln?
Wenn du die Texturen deaktivierst, dann kannst du entsprehend eine Variable an den Shader übergeben, dass Texturen gerade aus sind und dann dort unterscheiden, oder du lässt ein anderes Program ablaufen.
 
Wow! Mehr als ich zu hoffen wagte :D:top:

hab grad mal probiert, die methode glAttachShader zu verwenden... Chaos pur :(

1. Sprache Shader-Codes: keine Ahnung :read:, Beispiele in C++ hab ich wie gesagt nicht gefunden, aber das was in dem verlinkten Thread steht, unterscheidet sich doch grundlegend von meinem Shader-Inhalt, wie soll man das verwenden können? :confused:
2. OGL 2.0 müsste das sein
3. zweitrangig, aber wo gibts diese nvidia-klasse?

Ich stell, sobald ich daheim bin, mal das Chaos aus meiner Shader-Klasse hier rein. Das wird ein Spaß...

4. wie übergeb ich die Textur-Werte an den Shader?
Den Shader neu zu machen, wenn ich Lichtquellen ein- und ausschalte, ist beim Phong-Shading quasi pflicht, sonst rendert er Lichtquellen die nicht da sind... aber das braucht fast eine Sekunde, bis der Shader dann wieder läuft; zur Laufzeit ein Alptraum.

Außerdem ist das mit den Texturen eigentlich ganz simpel: Ich zeichne verschiedene Objekte, die alle per Shader gerendert werden sollen, aber nur manche davon sollen Texturen erhalten, sagen wir von 6 Objekten 3 und 4. Muss ich dann für 1+2 einen Shader ohne Texturen machen, für 3+4 einen Mit und für 5+6 wieder den vom Anfang? Das klingt für mich wie mit Kanonen auf Spatzen geschossen und auch noch verfehlt :ugly:

für jede Textur einen Shader zu erstellen kann ja auch nicht die Lösung sein ...
 
Was meinst du mit Textur-Werten? Texturen, Texturkoordinaten oder die Variablen, die angeben ob eine Textur vorhanden ist?

Den Shader neu zu machen, wenn ich Lichtquellen ein- und ausschalte, ist beim Phong-Shading quasi pflicht, sonst rendert er Lichtquellen die nicht da sind... aber das braucht fast eine Sekunde, bis der Shader dann wieder läuft; zur Laufzeit ein Alptraum.
:eek:

Schau dir mal an, wie du Variablen an die Programs übergibst (dazu gibt's z.B. glUniform1f, mit der du Werte an uniform-Variablen übergeben kannst). Du musst nur den Zustand der Lampen dem Shader mitteilen und dann dort entscheiden, welche Formel du zur Berechnung des Pixels nimmst. Mit den Texturen kannst du es auch so machen, oder du sorgst dafür, dass alle Objekte Texturiert sind (kann ja auch eine komplett weiße Textur sein). Den Shader neu zu erzeugen, nur weil du ein Licht umschaltest, ist wie du bemerkt hast, keine gute Lösung.

Das nvidia SDK findest du entweder mit google :p oder hier. Wobei du wohl die 10er-Version nehmen solltest, da die 11er im Moment nur D3D enthält (und OpenGL kommt "in the near future" ^^)
 
Auch wenn du in C(++) programmierst, das DelphiGL-Wiki ist eine ganz nette Anlaufstelle wenn es um OpenGl geht. Die OpenGL-Befehle sind bis auf kleinste Details identisch. Dort findest du mehrere komplette PhongShader als Beispiel mit Texturierung. Je nach Komplexität der Szene (z.B. Anzahl an Lichtquellen) kannst du beide ggf. modifizieren.
Shader 1
Shader 2

Wenn du ernsthaft weiterprogrammieren möchtest und auch auf die modernere OpenGL3-Architektur nutzen möchtest, dann kann ich dir folgendes Buch sehr empfehlen:
OpenGL SuperBible (5. Edition)
Das Buch geht sehr auf die Benutzung von Texturen, alle Arten von Buffern und auch auf die Grundlagen der Shadersprachen ein. Etwas dünn ist die Handhabung von Geometriedaten.

OpenGL3 hat allerdings den Nachteil, dass die Einstiegshürde ziemlich hoch liegt (kein Immidiate-Mode, keine Fixed-Functions für Beleuchtung, Nebel...), dafür entledigt sich man vieler Altlasten und bekommt viel mehr Kontrolle und Möglichkeiten. Mit festen Lichtquellen musst du dich nicht mehr rumärgern. Sie existieren einfach nicht mehr und du darfst sie selbst implementieren wie du es möchtest. :ugly:
 
Schau dir mal an, wie du Variablen an die Programs übergibst (dazu gibt's z.B. glUniform1f, mit der du Werte an uniform-Variablen übergeben kannst). Du musst nur den Zustand der Lampen dem Shader mitteilen und dann dort entscheiden, welche Formel du zur Berechnung des Pixels nimmst. Mit den Texturen kannst du es auch so machen, oder du sorgst dafür, dass alle Objekte Texturiert sind (kann ja auch eine komplett weiße Textur sein). Den Shader neu zu erzeugen, nur weil du ein Licht umschaltest, ist wie du bemerkt hast, keine gute Lösung.

Ich hab mal versucht, zwei Arrays von Bools zu übergeben, eins für die Texturen, eins für die Lichtquellen.
1. Bools kann man irgendwie garnicht übergeben?
2.
Code:
GLint loc = glGetUniformLocation(ProgramObject, "Scale");
if (loc != -1)
{
   glUniform1f(loc, 0.432);
}
The uniform location remains valid until you link the program again, so there is no need to call glGetUniformLocation every frame.
Genau das dort will ich ja nicht, sondern ich will eine Variable, die im Hauptprogramm geändert wird, sodass der Shader mitkriegt, was er zu tun hat. Aber irgendwie will mir die richtige Lösung einfach nicht einfallen :read:

3. Damit der Shader die Änderung an der Variable mitkriegt, müsste glUniform1f ja immer vom Hauptprogramm aus getriggert werden.. kann man zur Laufzeit quasi in den Shader eingreifen? Dann bräuchte ich ihn nicht neu machen..

Danke für die Geduld :angel: dieses Metier ist mir noch etwas fremd~

@Hasentod:
Ja, ich werde (und muss) weiter damit programmieren. Ich frag mal hier (auf der Arbeit) nach, ob das Buch hier rumfliegt, und falls nicht, bestell ichs vielleicht. Danke für den tip :top:
 
Das in 2. ist schon der richtige Weg, um vom Hauptprogramm zum Shader zu kommunizieren. Wenn du keine Bools übergeben kannst, dann benutz stattdessen z.B. float und überprüfe ob der float z.B. >0 ist.

ich will eine Variable, die im Hauptprogramm geändert wird, sodass der Shader mitkriegt, was er zu tun hat
Genau dafür benutzt du das glUniform. Der Shader kann nicht einfach eine Variable aus deinem Hauptprogramm überwachen. Du könntest dir jedoch eine Klasse bauen, die eine Uniform-Variable in einem Shader repräsentiert und den operator= so überladen, dass dann bei jeder Zuweisung glUniform aufgerufen wird, falls es dir da nur um die Syntax geht.
 
Na, da nimmt man aber keinen Float, weil der viel zu ressourcenhungrig ist, wenn man damit nur einen boolean repräsentieren will. Da bietet sich doch etwas kleineres, wie unsigned char an.

Die nvidia-Bibliothek läuft dann aber, wenn ich mich recht entsinne [ich bin mir da nicht ganz sicher] aber nur auf PCs, die auch ne nvidia karte haben.
 
Oookay, folgende Bilanz:

1. Ich war schon drauf und dran, in einem weiteren Post offenzulegen, dass absolut nichts funktioniert und das Programm die Variablen allesamt nicht liest, bis ein Kollege von mir:hy: auf die Lösung stieß:
Code:
glUseProgram(p);
glUniform1i(glGetUniformLocationARB(p, "lightVal0"), lights[0]);
Dass man die uniform-Variablen festlegen muss, nachdem man das "Programm" startet, muss einem auch erstmal gesagt werden.
2. Ich hab mich jetzt was in die Shader-Language reingelesen und das Ausgangsproblem gefixt (dazu musste ich nur die Änderungen bzw. Vorschläge von hier im Phong-Shader einbauen und schon vertrugen er und die Textur sich prima :irre: )

3. @Pitti: Wie du siehst hab ich keinen float, aber immer noch int... werd ich wohl demnächst noch abändern. Einerseits sollten die paar Bytes ja nicht viel ausmachen, aber wenn ich mir vor Augen halte, dass jeder Shader x-millionen Mal pro Sekunde aufgerufen wird, grauts mir :read:

im Endeffekt bin ich froh, dass es nun endlich läuft :D und jetzt werd ich mich erstmal mit den Texturen rumschlagen. mal schauen, ob die auch streiken werden...

Greetz

€dit: :rolleyes: @Pitti: wie kann man per glUniform{xx} denn characters oder ein character-array übergeben? glUniform1cv o.ä. gibts nicht
 
Zuletzt bearbeitet:
Mh, mir ging es eigentlich nur um die Repräsentation von bools mit einem float. Aber wenn du schon mit int arbeitest, sollte es eigentlich auch problemlos sein den int durch ein char zu ersetzen. Die sind alle integer datentypen. Nur das Int meist 4byte beansprucht, char nur 1byte...
[und dementsprechend nen kleineren Zahlenbereich repräsentiert. Das ist ja aber für de Repräsentation 0 oder ungleich 0 ziemlich wurst]

Mit der shader-language selbst kenne ich mich leider nicht aus :keks:
 
ich hab jetzt einen Array von GLints benutzt, bei GLuint hängt sich das Programm eigenartigerweise auf...
... und mein Shader funktioniert auf meinem Notebook von 2008, nicht aber auf meinem 3 monate alten PC. :keks: @ myself
da ich den ganzen Kram für die Arbeit mache und nich auf meinem Tower entwickeln kann, nervt das schon ganz gut... :read:

(openGL version ist die gleiche)
 
Zurück
Oben