SyntaxBomb - Indie Coders

Languages & Coding => BlitzMax / BlitzMax NG => MiniB3D => Topic started by: Krischan on January 02, 2020, 12:03:18 AM

Title: OpenB3D PBR Shader Demo
Post by: Krischan on January 02, 2020, 12:03:18 AM
First: Happy New Year! Second: reports about my withdrawal from Blitzbasic are strongly exaggerated, I've had a lot of other things to do in 2019 ;D But recently I have been working intensively with Blender 2.8, PBR, GLSL and Substance. The result is a small demo which I would like to share with you. I have created a small video and some screenshots, have fun with it.



The shader already looks quite good but I'm sure I've made some mistakes. First, the spotlight effect doesn't look right. I've played around a lot with it but couldn't make it any better. Second I'm sure there is a problem with the Parallax Occlusion mapping as it is only barely noticeable. Perhaps there is a problem with the Tangent/Worldspace coordinates.

If you want to take a look at the source by yourself, you can download it here with all textures and precompiled executables to run the demos in 1920x1080 windows:
PBR_ShaderDemo.zip (http://www.christianhart.de/bmax/PBR/PBR_ShaderDemo.zip) (20MB)

I would be very happy about any improvements and optimizations. ;) Oh, and Mark if you want you can use this or a simplified version of it in the OpenB3D demos.
Title: Re: OpenB3D PBR Shader Demo
Post by: Naughty Alien on January 02, 2020, 01:49:38 AM
very very nice man..
Title: Re: OpenB3D PBR Shader Demo
Post by: Derron on January 02, 2020, 09:38:58 AM
Are the bugs you mention what is visible at 1:15 of the video (and around 3:00 too) ?

Looking good so far - albeit I think bricks in old castles won't be so shiny new - or glossy from being so wet. They are worn out / old - almost breaking apart into the material they were made of. Exception is "natural" stone - which would be "rough" nonetheless - and only shiny in the metal or quartz vains they have built in.

Maybe having "rocks" (for nice bump/normal - and maybe tesselation presentation) and metal doors would be better to show stuff - might look "more realistic" then.
Or create some "water puddles" for reflection.

Also interesting to look (PBR wise) is ICE - so something with a different IOR (frozen glass ... ice ...) or stuff like "glowing iron" (so a fireplace of a smith with some iron glowing in).

As you have dynamic lights - maybe you could fake some fire (particles + some emitting and slightly moving/wobbling objects - or nonfaked, emissive particles).


Maybe this gave you some ideas for 2020 :)


PS: Blender 2.82 (or 81) help with their denoiser - except you are using Eevee already because it might look more similar to your engine than a rendering not using "faked" effects like Eevee does.
Title: Re: OpenB3D PBR Shader Demo
Post by: Krischan on January 02, 2020, 01:48:24 PM
> Are the bugs you mention what is visible at 1:15 of the video (and around 3:00 too)

Yes, that comes from the Parallax Occlusion function when you get too close to a wall - then the UVs are warped too much at very close distances. I think the function itself works, it must be something with the coordinate system.

I know that the bricks look artificial, it is very hard to find free suitable textures and I've just started working with Substance Designer, which is a very complex tool, so that's the best free texture I've found so far. I've started with my own textures from my game project and used the great tool Materialize (http://boundingboxsoftware.com/materialize/) to convert them to PBR and the results look quite good, but B2M3 (https://store.steampowered.com/app/325910/Substance_B2M3/) from Substance and Substance Designer (https://store.steampowered.com/app/1194120/Substance_Designer_2020/) produce much better results (they are currently on sale in Steam if you don't like the subscription Adobe introduced). I think it is better to create the texture from the scratch instead of converting a photo, but it takes a lot of time.

The metal demo is just an extreme example how different the reflections become with a metallic texture still using the same shader. My next goal ist to include everything into my RPG game project.

And I'd be happy if somebody could review the shader code. It was a hard piece of work to put it all together and I still don't understand some details in it.
Title: Re: OpenB3D PBR Shader Demo
Post by: iWasAdam on January 02, 2020, 03:22:01 PM
be aware that Substance (designer and the rest) have been bought by Adobe and are no longer free. The current status of them is unsure as to whether Adobe will can them completely (absorbing the tech into photoshop, etc) or offer them as part of their monthly subscriptions.  :(
They will be discontinued from Steam at some stage - probably not too far in the future.

Very nice looking - add some shadows and you've got a winner :)
Title: Re: OpenB3D PBR Shader Demo
Post by: Krischan on January 02, 2020, 03:57:48 PM
Regarding Substance: I know. The current steam version gets Updates until January 2021 and I don't need the source textures, which are not included if you go Steam. BUT: I've synchronized my Steam account with Substance and got separate license keys from them for each of their products I've bought, so I can install them without Steam. Let's see what will happen in one year.

Shadows: I tried to add the OpenB3D shadows but first it looks weird and the demo crashes soon. Something must be colliding with my shader as the shadows work without the shader applied and vice versa. Perhaps Mark can take a look at it.
Title: Re: OpenB3D PBR Shader Demo
Post by: Derron on January 02, 2020, 04:02:59 PM
When were Substance products free?

For PBR there are some node setup tutorials to create PBR from single textures in Blender. So it might be useable to generate textures from it.
I remember the time when people draw in their scratches and cavity maps by hand instead of one-clicking in SP.


Bye
Ron
Title: Re: OpenB3D PBR Shader Demo
Post by: Krischan on January 02, 2020, 04:16:58 PM
I think in a few years, Blender will become the main Open Source competitor for Substance. But not yet. After Adobe acquired Allegorithmic there were a lot of discussions. You can even discuss if it is worth to buy the Steam version(s) or go for subscription already (Steam: 233,61€ vs. 195,60€ subscription). And without a sale, Steam is even more expensive. But I don't like subscriptions and Steam is the only way to get a real license key at the moment, but with a one-year limited update plan.

I played a lot with Filter Forge and other tools but they are slow and the results are not so stunning like in Substance Designer. And I don't want to draw my textures only in Photoshop when it comes to PBR which needs the 3rd dimenson.
Title: Re: OpenB3D PBR Shader Demo
Post by: Derron on January 02, 2020, 05:53:19 PM
There are no real counterparts for Substance Painter at the moment - some say one should use ArmorPaint:
https://armorpaint.org (downloads there are 16$)
https://github.com/armory3d/armorpaint (build it on your own or use one of the older releases there - for free)

If you really enjoy creating your own materials then ArmorPaint and its node based approach might be a joy for you. If you prefer "premade" materials with some knobs to adjust  then go with Substance Painter (I know you talk about Substance Designer). Both allow layer/mask based workflows.

I wished there was an FOSS alternative to SP - with an almost identical workflow/layout. But it won't happen as it does not happen for Photoshop (best chances for now is "Krita" - has more attention than Gimp).


bye
Ron
Title: Re: OpenB3D PBR Shader Demo
Post by: Krischan on January 02, 2020, 05:58:28 PM
For Photoshop: ever tried https://www.photopea.com (https://www.photopea.com)? Not exactly Photoshop, but a well done clone for free.
Title: Re: OpenB3D PBR Shader Demo
Post by: Derron on January 02, 2020, 06:06:28 PM
Yes I tried that. It misses most of the vector part (it has shapes but vector manipulation is missing). Also disliking the "online" thing. Pressure sensitivity is something I did not get working with it too - albeit there seems to be support for it.

Will have to stay with my stone old PS for now ... via wine it sucks but has working pressure sensitivity. If I only do "mouse work" I prefer to run it in an old XP-VM as it does not have some window-manager-problems like in wine (tool panels behind main window) - or odd crashes.


bye
Ron
Title: Re: OpenB3D PBR Shader Demo
Post by: Qube on January 03, 2020, 09:34:28 AM
Wow! now that looks impressive. Very nice work indeed :)
Title: Re: OpenB3D PBR Shader Demo
Post by: Steve Elliott on January 03, 2020, 10:01:14 AM
Some very nice work here, and what could be the beginnings of a very atmospheric dungeon-like game.
Title: Re: OpenB3D PBR Shader Demo
Post by: Krischan on January 05, 2020, 09:11:29 AM
Some very nice work here, and what could be the beginnings of a very atmospheric dungeon-like game.

I'm working on that. ;D Beside that: are there no GLSL shader experts here to take a look at my problems? I hardly dare to post the problem at khronos.org community as we're using a "dead" language and the legacy version 1.30 / OpenGL 2.0 pipeline in OpenB3D and the guys will probably laugh at me. ::)
Title: Re: OpenB3D PBR Shader Demo
Post by: Naughty Alien on January 05, 2020, 09:14:14 AM
I think Gabor could give you a hand, but he is probably busy elsewhere..
Title: Re: OpenB3D PBR Shader Demo
Post by: iWasAdam on January 05, 2020, 10:58:31 AM
Quote
are there no GLSL shader experts here to take a look at my problems?
post the issues and we can always look at it and see what can be done :)
Title: Re: OpenB3D PBR Shader Demo
Post by: Krischan on January 05, 2020, 02:59:51 PM
Ok, the issues more detailed: 8)

Screenshot A+B: enabling the Parallax Occlusion Mapping (Key 5), the effect is only barely noticeable and when you get close to walls there is a massive distortion of the UV coordinates. But I don't know why. I've read a lot about POM and an important issue seems that your UV coordinates must be in the "right" space (tangent?), but I don't understand the examples I've found.

And in general, the spotlight looks strange, too flat compared to the fireball. I'd expect more reflection/depth "feedback" from the light there. And that the spotlight reflections change when I move the camera around. It looks like a pointlight which is only moving along the X/Z axis when I walk around but not with the camera rotation. You know what I mean?

Vertex  Shader:
Code:  (Unknown Language)
  1. #version 130
  2.  
  3. #define NUM_LIGHTS 5
  4.  
  5. // ----------------------------------------------------------------------------
  6. // Constants and Structs
  7. // ----------------------------------------------------------------------------
  8.  
  9. struct FloatArray {
  10.         float Float;
  11. };
  12.  
  13.  
  14. // ----------------------------------------------------------------------------
  15. // Output values to the Fragment Shader
  16. // ----------------------------------------------------------------------------
  17.  
  18. out vec2 Vertex_UV;
  19. out vec3 Vertex_Normal;
  20. out vec4 Vertex_Position;
  21. out vec3 Vertex_Eyevector;
  22.  
  23. out vec3 Vertex_LightDir[NUM_LIGHTS];
  24. out vec3 Vertex_LightColor[NUM_LIGHTS];
  25. out float Vertex_LightRange[NUM_LIGHTS];
  26.  
  27. out vec3 Vertex_AmbientColor;
  28.  
  29.  
  30. // ----------------------------------------------------------------------------
  31. // Attributes from the main program
  32. // ----------------------------------------------------------------------------
  33.  
  34. uniform vec2 texscale;
  35. uniform vec2 texoffset;
  36. uniform float ambFactor;
  37. uniform FloatArray lightradius[NUM_LIGHTS];
  38.  
  39.  
  40. // ----------------------------------------------------------------------------
  41. // Main Vertex Shader
  42. // ----------------------------------------------------------------------------
  43.  
  44. void main()
  45. {
  46.         // Normal, Fragment and UV coordinates
  47.         Vertex_Normal = normalize(gl_NormalMatrix * gl_Normal);
  48.         Vertex_Position = gl_ModelViewMatrix * gl_Vertex;
  49.         Vertex_UV = (gl_MultiTexCoord0.xy * texscale) + texoffset;
  50.        
  51.         // Eye vector
  52.         vec4 ecPosition = gl_ModelViewMatrix * gl_Vertex;
  53.         vec3 ecPosition3 = (vec3(ecPosition)) / ecPosition.w;
  54.         Vertex_Eyevector = -normalize(ecPosition3);
  55.  
  56.         // Light properties
  57.         for (int i = 0; i < NUM_LIGHTS; ++i)
  58.         {
  59.                 Vertex_LightDir[i] = gl_LightSource[i].position.xyz - Vertex_Position.xyz;
  60.                 Vertex_LightColor[i] = gl_LightSource[i].diffuse.rgb;
  61.                 Vertex_LightRange[i] = lightradius[i].Float * lightradius[i].Float;
  62.         }
  63.        
  64.         // Ambient color
  65.         Vertex_AmbientColor = gl_LightModel.ambient.rgb * ambFactor;
  66.  
  67.         gl_Position = ftransform();
  68. }

Fragement Shader:
Code:  (Unknown Language)
  1. #version 130
  2.  
  3. #define NUM_LIGHTS 5
  4.  
  5. // ----------------------------------------------------------------------------
  6. // Constants and Structs
  7. // ----------------------------------------------------------------------------
  8.  
  9. struct FloatArray {
  10.     float Float;
  11. };
  12.  
  13. const float PI = 3.14159265359;
  14. const vec2 PMheight = vec2(0.04,-0.02);
  15.  
  16.  
  17. // ----------------------------------------------------------------------------
  18. // Input Textures from the main program
  19. // ----------------------------------------------------------------------------
  20.  
  21. uniform sampler2D albedoMap;
  22. uniform sampler2D normalMap;
  23. uniform sampler2D roughnessMap;
  24. uniform sampler2D metallicMap;
  25. uniform sampler2D heightMap;
  26. uniform sampler2D aoMap;
  27. uniform sampler2D emissionMap;
  28.  
  29.  
  30. // ----------------------------------------------------------------------------
  31. // Attributes from the Vertex Shader
  32. // ----------------------------------------------------------------------------
  33.  
  34. in vec2 Vertex_UV;
  35. in vec3 Vertex_Normal;
  36. in vec4 Vertex_Position;
  37. in vec3 Vertex_Eyevector;
  38. in vec3 Vertex_LightDir[NUM_LIGHTS];
  39. in vec3 Vertex_LightColor[NUM_LIGHTS];
  40. in float Vertex_LightRange[NUM_LIGHTS];
  41. in vec3 Vertex_AmbientColor;
  42.  
  43. out vec4 FragColor;
  44.  
  45.  
  46. // ----------------------------------------------------------------------------
  47. // Attributes from the main program
  48. // ----------------------------------------------------------------------------
  49.  
  50. uniform float levelscale;
  51. uniform float gamma;
  52.  
  53. uniform float fogStart;
  54. uniform float fogRange;
  55. uniform float fogDensity;
  56. uniform vec3 fogColor;
  57.  
  58. uniform float AttA;
  59. uniform float AttB;
  60. uniform float flicker;
  61. uniform FloatArray lightradius[NUM_LIGHTS];
  62.  
  63. uniform int isMetal;
  64.  
  65. // ----------------------------------------------------------------------------
  66.  
  67. uniform int flagDM;
  68. uniform int flagFG;
  69. uniform int flagPB;
  70. uniform int flagTM;
  71. uniform int flagTL;
  72.  
  73. // ----------------------------------------------------------------------------
  74.  
  75. uniform int texAL;
  76. uniform int texNM;
  77. uniform int texRO;
  78. uniform int texME;
  79. uniform int texHM;
  80. uniform int texAO;
  81. uniform int texEM;
  82.  
  83.  
  84. // ----------------------------------------------------------------------------
  85. // Normal functions
  86. // ----------------------------------------------------------------------------
  87.  
  88. mat3 cotangent_frame(vec3 N, vec3 p, vec2 uv)
  89. {
  90.         vec3 dp1 = dFdx(p);
  91.         vec3 dp2 = dFdy(p);
  92.         vec2 duv1 = dFdx(uv);
  93.         vec2 duv2 = dFdy(uv);
  94.  
  95.         vec3 dp2perp = cross(dp2, N);
  96.         vec3 dp1perp = cross(N, dp1);
  97.         vec3 T = dp2perp * duv1.x + dp1perp * duv2.x;
  98.         vec3 B = dp2perp * duv1.y + dp1perp * duv2.y;
  99.  
  100.         float invmax = inversesqrt(max(dot(T, T), dot(B, B)));
  101.         return mat3(T * invmax, B * invmax, N);
  102. }
  103.  
  104. // ----------------------------------------------------------------------------
  105.  
  106. vec3 perturb_normal(vec3 N, vec3 V, vec3 map, vec2 texcoord)
  107. {
  108.         map = map * 255.0 / 127.0 - 128.0 / 127.0;
  109.         mat3 TBN = cotangent_frame(N, -V, texcoord);
  110.         return normalize(TBN * map);
  111. }
  112.  
  113.  
  114. // ----------------------------------------------------------------------------
  115. // Tonemapping functions
  116. // ----------------------------------------------------------------------------
  117.  
  118. vec3 ToneMapFilmic(vec3 color)
  119. {
  120.         vec4 vh = vec4(color, gamma);
  121.         vec4 va = 1.425 * vh + 0.05;
  122.         vec4 vf = (vh * va + 0.004) / (vh * (va + 0.55) + 0.0491) - 0.0821;
  123.         return vf.rgb / vf.www;
  124. }
  125.  
  126. // ----------------------------------------------------------------------------
  127.  
  128. vec3 ToneMapSimple(vec3 color)
  129. {
  130.         return pow(color / (color + vec3(1.0)), vec3(1.0 / gamma));
  131.        
  132. }
  133.        
  134. // ----------------------------------------------------------------------------
  135.  
  136. vec3 ToneMapExposure(vec3 color)
  137. {
  138.         color = exp(-1.0 / ( 2.72 * color + 0.15 ));
  139.         color = pow(color, vec3(1. / gamma));
  140.         return color;
  141. }
  142.  
  143. // ----------------------------------------------------------------------------
  144.  
  145. vec3 ToneMapPBR(vec3 color)
  146. {
  147.         // HDR tonemapping
  148.         color = color / (color + vec3(1.0));
  149.         // gamma correct
  150.         color = pow(color, vec3(1.0 / gamma));
  151.        
  152.         return color;
  153. }
  154.  
  155. // ----------------------------------------------------------------------------
  156.  
  157. vec3 Uncharted(vec3 x)
  158. {
  159.         float A = 0.15;
  160.         float B = 0.50;
  161.         float C = 0.10;
  162.         float D = 0.20;
  163.         float E = 0.02;
  164.         float F = 0.30;
  165.        
  166.         return ((x * (A * x + C * B) + D * E) / (x * (A * x + B) + D * F)) - E / F;
  167. }
  168.  
  169. // ----------------------------------------------------------------------------
  170.  
  171. vec3 ToneMapUncharted(vec3 color)
  172. {
  173.         color = Uncharted(color * 4.5) * (1.0 / Uncharted(vec3(11.2)));
  174.         color = pow(color, vec3(1.0 / gamma));
  175.         return color;
  176.  
  177. }
  178.  
  179. // ----------------------------------------------------------------------------
  180.  
  181. vec3 ToneMapSCurve(vec3 x)
  182. {
  183.         //x = pow(x, vec3(1.0 / 2.2));
  184.        
  185.         float a = 2.51f;
  186.         float b = 0.03f;
  187.         float c = 2.43f;
  188.         float d = 0.59f;
  189.         float e = 0.14f;
  190.         return clamp((x * (a * x + b)) / (x * (c * x + d) + e), 0.0, 1.0);
  191. }
  192.  
  193.  
  194. // ----------------------------------------------------------------------------
  195. // PBR functions
  196. // ----------------------------------------------------------------------------
  197.  
  198. float DistributionGGX(vec3 N, vec3 H, float roughness)
  199. {
  200.         float a = roughness * roughness;
  201.         float a2 = a * a;
  202.         float NdotH = max(dot(N, H), 0.0);
  203.         float NdotH2 = NdotH * NdotH;
  204.  
  205.         float nom = a2;
  206.         float denom = (NdotH2 * (a2 - 1.0) + 1.0);
  207.         denom = PI * denom * denom;
  208.  
  209.         return nom / denom;
  210. }
  211.  
  212. // ----------------------------------------------------------------------------
  213.  
  214. float GeometrySchlickGGX(float NdotV, float roughness)
  215. {
  216.         float r = (roughness + 1.0);
  217.         float k = (r * r) / 8.0;
  218.  
  219.         float nom = NdotV;
  220.         float denom = NdotV * (1.0 - k) + k;
  221.  
  222.         return nom / denom;
  223. }
  224.  
  225. // ----------------------------------------------------------------------------
  226.  
  227. float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness)
  228. {
  229.         float NdotV = max(dot(N, V), 0.0);
  230.         float NdotL = max(dot(N, L), 0.0);
  231.         float ggx2 = GeometrySchlickGGX(NdotV, roughness);
  232.         float ggx1 = GeometrySchlickGGX(NdotL, roughness);
  233.  
  234.         return ggx1 * ggx2;
  235. }
  236.  
  237. // ----------------------------------------------------------------------------
  238.  
  239. vec3 fresnelSchlick(float cosTheta, vec3 F0)
  240. {
  241.         if(cosTheta > 1.0)
  242.                 cosTheta = 1.0;
  243.         float p = pow(1.0 - cosTheta,5.0);
  244.         return F0 + (1.0 - F0) * p;
  245. }
  246.  
  247.  
  248. // ----------------------------------------------------------------------------
  249. // Parallax Occlusion Mapping
  250. // ----------------------------------------------------------------------------
  251.  
  252. vec2 ParallaxOcclusionMapping(vec2 texCoords, vec3 viewDir)
  253. {
  254.         // number of depth layers
  255.         const vec2 heightScale = vec2(0.00625,-0.00625);
  256.         const float minLayers = 8;
  257.         const float maxLayers = 32;
  258.         float numLayers = mix(maxLayers, minLayers, abs(dot(vec3(0.0, 0.0, 1.0), viewDir)));
  259.        
  260.         // calculate the size of each layer
  261.         float layerDepth = 1.0 / numLayers;
  262.        
  263.         // depth of current layer
  264.         float currentLayerDepth = 0.0;
  265.        
  266.         // the amount to shift the texture coordinates per layer (from vector P)
  267.         vec2 P = viewDir.xy / viewDir.z * heightScale;
  268.         vec2 deltaTexCoords = P / numLayers;
  269.  
  270.         // get initial values
  271.         vec2  currentTexCoords = texCoords;
  272.         float currentDepthMapValue = texture(heightMap, currentTexCoords).r;
  273.      
  274.         while(currentLayerDepth < currentDepthMapValue)
  275.         {
  276.                 // shift texture coordinates along direction of P
  277.                 currentTexCoords -= deltaTexCoords;
  278.                 // get depthmap value at current texture coordinates
  279.                 currentDepthMapValue = texture(heightMap, currentTexCoords).r;  
  280.                 // get depth of next layer
  281.                 currentLayerDepth += layerDepth;  
  282.         }
  283.    
  284.         // get texture coordinates before collision (reverse operations)
  285.         vec2 prevTexCoords = currentTexCoords + deltaTexCoords;
  286.  
  287.         // get depth after and before collision for linear interpolation
  288.         float afterDepth = currentDepthMapValue - currentLayerDepth;
  289.         float beforeDepth = texture(heightMap, prevTexCoords).r - currentLayerDepth + layerDepth;
  290.  
  291.         // interpolation of texture coordinates
  292.         float weight = afterDepth / (afterDepth - beforeDepth);
  293.         vec2 finalTexCoords = prevTexCoords * weight + currentTexCoords * (1.0 - weight);
  294.  
  295.         return finalTexCoords;
  296. }
  297.  
  298.  
  299. // ----------------------------------------------------------------------------
  300. // Light functions
  301. // ----------------------------------------------------------------------------
  302.  
  303. float LinearizeDepth(float depth) // Note that this ranges from [0,1] instead of up to 'far plane distance' since we divide by 'far'
  304. {
  305.         float near = 0.1;
  306.         float far = fogRange*1.0/levelscale;
  307.         float z = depth * 2.0 - 1.0; // Back to NDC
  308.         return (2.0 * near) / (far + near - z * (far - near)); 
  309. }
  310.  
  311. // ----------------------------------------------------------------------------
  312.  
  313. float CalcAtt(float distance)
  314. {
  315.         return 1.0 / (1.0 + AttA * distance + AttB * distance * distance);
  316. }
  317.  
  318. // ----------------------------------------------------------------------------
  319.  
  320. float spotlight(int i, float attenuation, vec3 L, float intensity)
  321. {
  322.         float clampedCosine = max(0.0, 1.0 * dot(-L, gl_LightSource[i].spotDirection));
  323.         attenuation = attenuation * pow(clampedCosine, gl_LightSource[i].spotExponent*intensity);
  324.  
  325.         return attenuation;
  326. }
  327.  
  328.  
  329. // ----------------------------------------------------------------------------
  330. // Main Fragment Shader
  331. // ----------------------------------------------------------------------------
  332.  
  333. void main()
  334. {
  335.         vec2 uv = Vertex_UV;
  336.                
  337.         // Parallax Mapping
  338.         if(texHM > 0){uv = ParallaxOcclusionMapping(uv, normalize(Vertex_Eyevector.xyz));}
  339.        
  340.         // 1. Albedo Texture
  341.         vec4 albedo = vec4(0.5, 0.5, 0.5, 1.0);
  342.         if(texAL > 0){albedo = texture(albedoMap, uv);}
  343.        
  344.         // 2. Normalmap Texture
  345.         vec3 nrm = Vertex_Normal;
  346.         if(texNM > 0){nrm = texture(normalMap, uv).rgb;}
  347.                
  348.         // 3. Roughness Texture
  349.         float roughness = 0.7;
  350.         if(texRO > 0){roughness = texture(roughnessMap, uv).r;}
  351.  
  352.         // 4. Metallic Texture
  353.         float metallic = 0.2;
  354.         if(texME > 0){metallic = texture(metallicMap, uv).r;}
  355.  
  356.         // 5. Ambient Occlusion Texture
  357.         float ao = 1.0;
  358.         if(texAO > 0){ao = texture(aoMap, uv).r;}
  359.                
  360.         // 6. Emissive Texture
  361.         vec3 emission = vec3(0.0);
  362.         if(texEM > 0){emission = texture(emissionMap, uv).rgb * (1.0 + (flicker / 2.0)) * 4.0;}
  363.  
  364.         vec3 Lo = vec3(0.0);
  365.        
  366.         // ambient and emission lighting
  367.         vec3 color = (emission + Vertex_AmbientColor) * albedo.rgb;
  368.         vec3 r = vec3(0.0);
  369.        
  370.         // PBR active
  371.         if(flagPB > 0)
  372.         {
  373.        
  374.                 vec3 N = Vertex_Normal.xyz;
  375.                 vec3 V = normalize(Vertex_Eyevector.xyz);
  376.                 vec3 PN = perturb_normal(N, V, nrm, uv);
  377.  
  378.                 // calculate reflectance at normal incidence; if dia-electric (like plastic) use F0
  379.                 // of 0.04 and if it's a metal, use the albedo color as F0 (metallic workflow)    
  380.                 vec3 F0 = vec3(0.04);
  381.                 F0 = mix(F0, albedo.rgb, metallic);
  382.                 if(isMetal > 0){F0 = albedo.rgb;}
  383.                
  384.                 for(int i = 0; i < NUM_LIGHTS; ++i)
  385.                 {
  386.                         // calculate per-light radiance
  387.                         vec3 L = normalize(Vertex_LightDir[i]);
  388.                         vec3 X = normalize(gl_LightSource[0].spotDirection.xyz);
  389.                        
  390.                        
  391.                         vec3 H = normalize(V + L);
  392.                         float distance = length(Vertex_LightDir[i]) * 1.0 / levelscale;
  393.                         float attenuation = CalcAtt(distance) * Vertex_LightRange[i];
  394.                        
  395.                         // first light = player spotlight
  396.                         if(i == 0 && flagTL == 1)
  397.                         {
  398.                                 attenuation = spotlight(0, attenuation, L, 1.0);
  399.                                 attenuation = pow(attenuation / (attenuation + 1.0 + flicker), 1.0 / gamma) * (2.0 - 2.0 / Vertex_LightRange[i]);
  400.                         }
  401.                        
  402.                         if(i == 0 && flagTL == 0)
  403.                         {
  404.                                 attenuation = 1.0 / (1.0 + 0.0 * distance + 0.5 * distance * distance);
  405.                         }
  406.                        
  407.                         // light color (scaled)
  408.                         vec3 radiance = Vertex_LightColor[i] * attenuation;
  409.                                
  410.                         // Cook-Torrance BRDF
  411.                         float NDF = DistributionGGX(PN, H, roughness);
  412.                         float G = GeometrySmith(PN, V, L, roughness);
  413.                         vec3 F = fresnelSchlick(max(dot(H, V), 0.0), F0);
  414.                    
  415.                         vec3 nominator = NDF * G * F;
  416.                         float denominator = 2 * max(dot(PN, V), 0.0) * max(dot(PN, L), 0.0) + 0.001; // 0.001 to prevent divide by zero.
  417.                         vec3 specular = nominator / denominator;
  418.                
  419.                         // kS is equal to Fresnel
  420.                         vec3 kS = F;
  421.                         // for energy conservation, the diffuse and specular light can't
  422.                         // be above 1.0 (unless the surface emits light); to preserve this
  423.                         // relationship the diffuse component (kD) should equal 1.0 - kS.
  424.                         vec3 kD = clamp(vec3(1.0) - kS, 0.0, 1.0);
  425.                         // multiply kD by the inverse metalness such that only non-metals
  426.                         // have diffuse lighting, or a linear blend if partly metal (pure metals
  427.                         // have no diffuse light).
  428.                         kD *= 1.0 - metallic;
  429.                        
  430.        
  431.                         // non-player light: check backface lighting
  432.                         float NdotL = 1.0;
  433.                         if(i > 0)
  434.                         {
  435.                                 float NdotL = max(dot(PN, L), 0.0);
  436.                                 if(NdotL > 0.0)
  437.                                 {
  438.                                         Lo += (kD * albedo.rgb / PI + specular) * radiance * NdotL * attenuation;
  439.                                         r += attenuation;
  440.  
  441.                                 }
  442.                         }
  443.                                 // player light: simpler light equotation
  444.                         else
  445.                         {
  446.                                 Lo += Lo += (albedo.rgb / PI + specular) * radiance * attenuation;
  447.                                 r += attenuation;
  448.                         }
  449.  
  450.                 }
  451.         }
  452.        
  453.         // PBR off
  454.         else
  455.         {
  456.                 for(int i = 0; i < NUM_LIGHTS; ++i)
  457.                 {
  458.                         vec3 L = normalize(Vertex_LightDir[i]);
  459.                         vec3 N = Vertex_Normal.xyz;
  460.                         float NdotL = max(dot(N, L), 0.0);
  461.  
  462.                         float distance = length(Vertex_LightDir[i]) * 1.0 / levelscale;
  463.                         float attenuation = CalcAtt(distance) * Vertex_LightRange[i];
  464.  
  465.                         // first light = player spotlight
  466.                         if(i == 0 && flagTL == 1)
  467.                         {
  468.                                 attenuation = spotlight(0, attenuation, L, 2.0);
  469.                                 attenuation = pow(attenuation / (attenuation + 1.0 + flicker), 1.0 / gamma) * (2.0 - 2.0 / Vertex_LightRange[i]);
  470.                         }
  471.                        
  472.                         if(i == 0 && flagTL == 0)
  473.                         {
  474.                                 attenuation = 1.0 / (1.0 + 0.0 * distance + 0.5 * distance * distance);
  475.                         }                              
  476.  
  477.                         if(NdotL > 0.0)
  478.                         {
  479.                                 Lo += albedo.rgb * Vertex_LightColor[i] * attenuation * NdotL;
  480.                                 r += attenuation;
  481.                         }
  482.  
  483.                 }
  484.         }
  485.        
  486.         // put it all together
  487.         color += (color + Lo) * ao;
  488.        
  489.         // Tonemapping
  490.         if(flagTM == 1){color = ToneMapFilmic(color);}
  491.         if(flagTM == 2){color = ToneMapSimple(color);}
  492.         if(flagTM == 3){color = ToneMapExposure(color);}
  493.         if(flagTM == 4){color = ToneMapPBR(color);}
  494.         if(flagTM == 5){color = ToneMapUncharted(color);}
  495.         if(flagTM == 6){color = ToneMapSCurve(color);}
  496.        
  497.         // fog
  498.         if(flagFG > 0)
  499.         {
  500.                 float plane;
  501.                 plane = length(Vertex_Position);   // range-based (radial), flat would be plane = abs(position.z)
  502.                 float fogFactor = clamp((fogRange - plane) / (fogRange - fogStart), 0.0, 1.0);
  503.                
  504.                 // distant lights shine through the fog a little bit
  505.                 vec3 fogFactorX = max(clamp(r, 0.0, 0.5), vec3(fogFactor)) * (1.0 - fogDensity);
  506.                
  507.                 color = (fogColor * (1.0 - fogFactorX) + (color.rgb * fogFactorX));
  508.  
  509.         }
  510.        
  511.         // simple depthmap
  512.         if(flagDM > 0)
  513.         {
  514.                 float depth = 1.0 - LinearizeDepth(gl_FragCoord.z);
  515.                 color.rgb = vec3(depth);
  516.         }
  517.        
  518.         FragColor = vec4(color, albedo.a);
  519.  
  520. }
SimplePortal 2.3.6 © 2008-2014, SimplePortal