Check alle échte Black Friday-deals Ook zo moe van nepaanbiedingen? Wij laten alleen échte deals zien
Toon posts:

[GLSL] modulair shaden?

Pagina: 1
Acties:

Verwijderd

Topicstarter
hallo tweaknerds!

ik wil fatsoenlijke shader support in mn engine maar weet niet echt hoe ik dit modulair opzet (als dat uberhaupt mogelijk is)

bijv; ik heb
x polies die gewoon getextured moeten worden
x polies die getextured moeten worden en waar een specular map op wordt toegepast
x polies die getextured moeten worden en waar een normal map op wordt toegepast
x polies die getextured moeten worden en waar een normal map en specular map op wordt toegepast

etc.

nu kan ik 100 shaders voor alle combinaties gaan schrijven, maar dat lijkt me puristisch gezien wat redundant :P

wat ik eigenlijk wil is een los stukje shadercode schrijven voor elk onderdeel, specular / normal / etc en die dan op de een of andere manier samenvoegen om het gewenste materiaal te verkrijgen. maar geen idee hoe ik dit implementeer. moet ik met deze losse stukjes alsnog alle combinaties van shaderprograms compileren of kan dit op een betere manier?

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 19-11 23:43

.oisyn

Moderator Devschuur®

Demotivational Speaker

Verwijderd schreef op woensdag 27 februari 2008 @ 15:23:
moet ik met deze losse stukjes alsnog alle combinaties van shaderprograms compileren
Dat is wel wat iedereen doet :). Voor de basale dingen kun je natuurlijk branching gebruiken aan de hand van shader constants.

Wat wij hebben (voor oa Tomb Raider: Underworld) is een material editor waarin de artist zijn eigen material kan maken door verschillende componenten bij elkaar te slepen. Dit kunnen low-level bereken-componenten zijn (zoals vermenigvuldigen, optellen, etc. maar ook dingen als texture fetches) of hele complexe componenten (zoals een geheel belichtingsmodel gegeven een aantal inputs). Die componenten zijn eigenlijk gewoon HLSL functies, en bij het genereren van de shader wordt gewoon eerst de HLSL sourcecode gegenereerd en dat wordt door de compiler gehaald.

Sommige eigenschappen van een material zijn vast gedefinieerd, anderen kunnen ingevuld worden door een object waarop dat material geplakt zit. Denk aan textures, constanten, etc.. En dan zijn er nog bepaalde eigenschappen die door de engine worden ingevuld, zoals het aantal lichten en de eigenschappen daarvan. Dit zijn natuurlijk allemaal vertex- en pixelshader constants (en texture slots) die at runtime worden ingevuld en die zorgen dus niet voor heel veel verschillende combinaties. Zo hebben we een standaard material, die je een of meerdere diffuse, specular, normal en emmisive textures mee kunt geven, alsmede nog wat andere properties (zoals factoren en constanten). Dit is in feite een enkele shader, en aan de hand van boolean constants worden bepaalde dingen aan of uit gezet.

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • CRiMiNaL
  • Registratie: Mei 2002
  • Laatst online: 10-01-2024

CRiMiNaL

Witlof ^^

Ik zou het shader schrijven gewoon in HLSL doen in programma's zoals FX Composer, daarnaast in je engine een mogelijkheid bieden tot het toekennen van shaders aan een mesh en het importeren/gebruiken van die shader files. Welke formaat je wil gaan gebruiken moet je zelf even kijken, FX Composer (en z'n tegenhanger van ATI) bieden erg veel verschillende formaten waar je je shaders naartoe kan exporteren.

Het vergemakkelijkt namenlijk het maken van shaders en het scheelt je een hoop prog. werk voor shaders die daar al standaard bij geleverd zitten (denk aan, blinn, phong en lambert).

Zit me even af te vragen in hoeverre OpenGL met HLSL overweg kan, maar volgens mij zit dat net zo goed als DirectX.

... MMORPG Addict.


Verwijderd

Topicstarter
bedankt voor de reacties :)

volgens mij kan opengl alleen overweg met glsl, maar basically is dat wel hetzelfde als hlsl voor zover ik weet :) schrijf trouwens liever zelf mn shaders, hoewel ik wel shader designer gebruik om het snel te kunnen previewen.

.oisyn; beetje dom van me dat ik het boolean gebeuren niet heb bedacht, lijkt me een goed plan :) (bump true/false, specularmap true/false, etc, moet ik het allemaal wel mee kunnen doen)
Die componenten zijn eigenlijk gewoon HLSL functies, en bij het genereren van de shader wordt gewoon eerst de HLSL sourcecode gegenereerd en dat wordt door de compiler gehaald.
dit staat los van de boolean methode neem ik aan? maar als er nou voor elk material een shader prog zou moeten worden gecompileerd op basis van de material properties, krijg je dan niet veel te veel shaders? (alsin; wordt dat niet ubertraag?)

btw oisyn, is er ook maar IETS dat je NIET weet? :P _/-\o_

[ Voor 6% gewijzigd door Verwijderd op 27-02-2008 16:43 ]


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 19-11 23:43

.oisyn

Moderator Devschuur®

Demotivational Speaker

Verwijderd schreef op woensdag 27 februari 2008 @ 16:43:
dit staat los van de boolean methode neem ik aan?
Idd. Dit is het bij elkaar slepen van materials aan de hand van componenten. Je krijgt dan verschillende materials, met elk hun eigen shader.
maar als er nou voor elk material een shader prog zou moeten worden gecompileerd op basis van de material properties, krijg je dan niet veel te veel shaders? (alsin; wordt dat niet ubertraag?)
Geh. In de early days, zeg een paar jaar terug, toen SM 2.0 nog hoogtij vierde en branching niet altijd kon / handig was, implementeerden we de booleans ook @ compiletime. Dat wil zeggen, van elke combinatie van settings werd een shader gegenereerd. En dat keer 4 om een material met 0, 1, 2 of 3 lights te kunnen renderen. En zo hadden we een shadertabel met, zeg, tienduizenden verschillende shaders ;). De traagheid valt wel mee, je gebruikt natuurljik lang niet alle shaders @ runtime, en je laadt alleen die in die je gebruikt. nVidia drivers konden wel enorm roet in het eten gooien trouwens (sinds de Geforce 5), omdat ze de shadercode gingen proberen te optimaliseren (en natuurlijk niet bij het inladen, maar zodra je er iets mee wilt renderen). Dat zorgde voor enorme stalls tijdens het runnen van je game als je een nieuw gebied inliep oid. Dus hebben zij de driver aangepast zodat die optimalisaties niet gedaan werden als je Tomb Raider: Legend draaide ;)
btw oisyn, is er ook maar IETS dat je NIET weet? :P _/-\o_
Weet ik niet. Oh, dat dus :+

[ Voor 4% gewijzigd door .oisyn op 27-02-2008 18:13 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Verwijderd

Topicstarter
haha meesterlijk
maar het is dus technisch zowaar mogelijk om ziek veel shaders 'ingeladen' te hebben.. ik vermoedde dat na een stuk of 10 de boel wel vol zou slibben :P
is er nog een voordeel bij het compilen van 4 shaders met alle combo's van 2 booleans tegenover het hebben van 1 shader waarin je die booleans gewoon set van uit c++ met glUniform1i ?
op die laatste manier heb ik het nu, en ik moet zeggen, het hakkelt er over :| (maar dat kan ook aan 100 andere dingen liggen die ik niet goed doe :P)

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 19-11 23:43

.oisyn

Moderator Devschuur®

Demotivational Speaker

Je moet even opletten wat voor assembly je GLSL daadwerkelijk genereert bij if-statements. Er zijn 3 opties: geen branching, static branching en dynamic branching. Als er voor branching wordt gekozen wil je dat het in dit geval static is - de driver kan dat beter optimizen en het is sowieso wat sneller omdat van tevoren al het juiste pad gekozen kan worden (de bool constant kan immers niet zomaar veranderen). Wat je iig niet wilt is dat ie helemaal niet brancht. De GLSL compiler genereert dan effectief code dat alle codepaden uit zal voeren, en vervolgens de subresultaten met 0 of 1 gaat vermenigvuldigen afhankelijk van of de boolean op false of true staat. Ik ken GLSL verder niet goed, maar er zijn vast opties om aan te geven wat voor code er gegenereerd moet worden.

Afgezien van dat maakt het denk ik niet zo heel veel uit. Het instellen van wat bool constants is over het algemeen ietsjes sneller als het instellen van een hele nieuwe shader, maar dat zal marginaal zijn.

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Verwijderd

Topicstarter
ik heb lang gezocht naar het branching gebeuren maar kan er nergens iets over vinden qua glsl.. ik vrees toch dat het niet aanpasbaar is in glsl :|
het lijkt me niet dat dat de bottleneck is van mijn fps (zonder shader ongeveer 20, met shader rond de 3) maar om het zeker te weten zal ik morgen eens alles met een simpele shader renderen en een paar objecten met de normal- en specularmapshader.

hier trouwens de code van de shader zoals ik hem nu heb; tangents gaan via texcoord5. alles wordt gedaan in tangentspace, weet alleen niet zeker of ik dat helemaal goed doe.. lijkt nog niet helemaal te kloppen qua visuals iig. (stukje over ts_eye snap ik niet helemaal, zo wordt het gedaan in een tutorial van ozone maar dat is met nondirectional lights, mijn (enige) licht is wel directioneel.)

vertex shader:
C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
varying vec3 ts_light;
varying vec3 ts_eye;
varying vec3 ts_normal;

varying float fog_factor;

void main() {
  gl_Position = ftransform();
  
  gl_TexCoord[0] = gl_MultiTexCoord0;
  gl_TexCoord[1] = gl_MultiTexCoord1;
  gl_TexCoord[2] = gl_MultiTexCoord2;
  gl_TexCoord[3] = gl_MultiTexCoord3;
  gl_TexCoord[4] = gl_MultiTexCoord4;
  gl_TexCoord[5] = gl_MultiTexCoord5;
  gl_TexCoord[6] = gl_MultiTexCoord6;
  gl_TexCoord[7] = gl_MultiTexCoord7;
  
  vec3 normal = normalize(gl_NormalMatrix * gl_Normal);
  vec3 tangent = normalize(gl_NormalMatrix * gl_TexCoord[4].xyz);
  vec3 bitangent = cross(normal, tangent);
  vec3 vertex = vec3(gl_ModelViewMatrix * gl_Vertex);
  
  ts_normal.x = dot(normal, tangent);
  ts_normal.y = dot(normal, bitangent);
  ts_normal.z = dot(normal, normal);
  ts_normal = normalize(ts_normal);
  
  vec3 tmp_light = gl_LightSource[0].position.xyz;
  ts_light.x = dot(tmp_light, tangent);
  ts_light.y = dot(tmp_light, bitangent);
  ts_light.z = dot(tmp_light, normal);
  ts_light = normalize(ts_light);
  
  vec3 tmp_vertex = -vertex;
  ts_eye.x = dot(tmp_vertex, tangent);
  ts_eye.y = dot(tmp_vertex, bitangent);
  ts_eye.z = dot(tmp_vertex, normal);
  ts_eye = normalize(ts_eye);
  
  // fog
  const float LOG2 = 1.442695;
  gl_FogFragCoord = length(vertex.xyz) * 0.0004;
  fog_factor = exp2(-gl_Fog.density *
                     gl_Fog.density *
                     gl_FogFragCoord *
                     gl_FogFragCoord * LOG2);
  fog_factor = clamp(fog_factor, 0.0, 1.0);
}


C:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
varying vec3 ts_light;
varying vec3 ts_eye;
varying vec3 ts_normal;

uniform sampler2D map_texture;
uniform sampler2D map_normal;
uniform sampler2D map_specular;

uniform int bool_map_normal;
uniform int bool_map_specular;

varying float fog_factor;

void main() {
  ts_light = normalize(ts_light);
  ts_eye = normalize(ts_eye);
  ts_normal = normalize(ts_normal);
  
  // maps
  vec4 fragment_normal = vec4(0.0);
  vec4 fragment_texture = texture2D(map_texture, gl_TexCoord[0].xy);
  
  // lighting calculation
  vec4 color_ambient = gl_LightSource[0].ambient * gl_FrontMaterial.ambient;
  
  float amount_diffuse;
  if (bool_map_normal == 1) {
    fragment_normal = normalize(texture2D(map_normal, gl_TexCoord[0].xy) * 2.0 - 1.0);
    amount_diffuse = max(dot(ts_light.xyz, fragment_normal.xyz), 0.0);
  } else {
    amount_diffuse = max(dot(ts_light.xyz, ts_normal.xyz), 0.0);
  }
  vec4 color_diffuse = gl_LightSource[0].diffuse * gl_FrontMaterial.diffuse * amount_diffuse;
  
  float amount_specular;
  if (bool_map_specular == 1) {
    vec4 fragment_specular = texture2D(map_specular, gl_TexCoord[0].xy);
    if (bool_map_normal == 1) {
      amount_specular = pow(clamp(dot(reflect(-ts_light.xyz, fragment_normal.xyz), ts_eye), 0.0, 1.0), gl_FrontMaterial.shininess) * fragment_specular.x;
    } else {
      amount_specular = pow(clamp(dot(reflect(-ts_light.xyz, ts_normal.xyz), ts_eye), 0.0, 1.0), gl_FrontMaterial.shininess) * fragment_specular.x;
    }
  } else {
    amount_specular = 0.0;
  }
  vec4 color_specular = gl_LightSource[0].specular * gl_FrontMaterial.specular * amount_specular;
  
  float contrast = 2.2;
  float gamma = -0.24;
  
  vec4 finalcolor = clamp((fragment_texture * color_ambient +
                           fragment_texture * color_diffuse +
                                              color_specular) * contrast + gamma, 0.0, 1.0);
  gl_FragColor = mix(gl_Fog.color, finalcolor, fog_factor);
}

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 19-11 23:43

.oisyn

Moderator Devschuur®

Demotivational Speaker

Dit zijn nou niet echt heel erg spannende shaders. Wat voor videokaart heb je, en hoeveel pixels teken je ongeveer per frame (wat is je resolutie, hoeveel polygonen teken je en wat is de gemiddelde orde van grootte van een polygoon)? Heb je veel overdraw (liggen er veel polygonen achter elkaar en teken je ze ook nog eens min of meer van achter naar voren)?

Probeer je batches eens ruwweg te sorteren van voor naar achter. Of doe eerst een depth-pass waarbij je colorwrites uit zet en een simpele (lege) shader gebruikt (of alleen een gl_FragColor = vec4(0) als je verplicht bent een kleur te outputten in je shader), en teken daarna de polygonen nog een keer met je shaders en een depth func van z-equal. Het liefst doe je trouwens allebei (eerst een ruw gesorteerde depth-pass, en daarna een render pass). Hiermee krijg je ten eerste het minste overdraw, en ten tweede is de overdraw alleen in de depth pass en dus is het gigantisch veel goedkoper omdat je pixelshader op dat moment niets kost. Controleer trouwens ook of je alpha-blending uit hebt staan.

Overigens een kleine opmerking, wat je daar toepast heet brightness, geen gamma. Gamma is een exponent op je kleurwaarden, en bij 1 zijn die dus lineair :)

[ Voor 17% gewijzigd door .oisyn op 28-02-2008 12:52 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 19-11 23:43

.oisyn

Moderator Devschuur®

Demotivational Speaker

Oh, zijn je textures trouwens wel gewoon gemipmapped? Wel doen, want cache misses zijn enorm duur, en kan makkelijk je verschil in framerate verklaren.

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Verwijderd

Topicstarter
videokaart: geforce 5 (fx 5600)
res: 1280x640
triangles: ongeveer 12000 (btw; in gllists) waarvan slechts ongeveer 2% met normal- en specular map
mipmaps aan
alpha blending uit
nog niet gesorteerd (dus beetje random order)

hoe werkt dat depthpass renderen, kan ik vrolijk eerst alles met de simpele shader tekenen en daarna nog een keer met de normale, of moet ik ergens aangeven dat hij de depth buffer van de eerste render gebruikt? op de eerste manier maakt het iig niets uit qua fps.. (simpele shader z less or equal, 2e z equal)

ik ga de boel nu sorteren voor het renderen, hoewel dat door de glllist alleen per lijst kan.. maar alle beetjes helpen :)

en tnx voor de comment over de gamma/brightness.. moet natuurlijk wel correct blijven :*)

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 19-11 23:43

.oisyn

Moderator Devschuur®

Demotivational Speaker

Hij gebruikt automatisch al de depth buffer die je tijdens de depth pass gebruikt, dus daar hoef je idd niets voor op te geven. Het is wel handig om nog even z-writes uit te zetten tijdens de render pass, dat scheelt weer performance (ook al zijn de waarden die geschreven worden identiek aan degene die uitgelezen zijn)

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.

Pagina: 1