Quellcode durchsuchen

improved pbr shader and material

danaugrs vor 7 Jahren
Ursprung
Commit
adab8aaad1

+ 10 - 6
gls/uniform.go

@@ -25,9 +25,14 @@ func (u *Uniform) Init(name string) {
 	u.lastIndex = -1 // invalid index
 }
 
-// Location returns the location of this uniform for
-// the current shader program
-// The returned location can be -1 if not found
+// Name returns the uniform name.
+func (u *Uniform) Name() string {
+
+	return u.name
+}
+
+// Location returns the location of this uniform for the current shader program.
+// The returned location can be -1 if not found.
 func (u *Uniform) Location(gs *GLS) int32 {
 
 	handle := gs.prog.Handle()
@@ -38,9 +43,8 @@ func (u *Uniform) Location(gs *GLS) int32 {
 	return u.location
 }
 
-// LocationIdx returns the location of this indexed uniform for
-// the current shader program
-// The returned location can be -1 if not found
+// LocationIdx returns the location of this indexed uniform for the current shader program.
+// The returned location can be -1 if not found.
 func (u *Uniform) LocationIdx(gs *GLS, idx int32) int32 {
 
 	if idx != u.lastIndex {

+ 4 - 15
light/spot.go

@@ -17,12 +17,11 @@ type Spot struct {
 	core.Node                // Embedded node
 	color     math32.Color   // Light color
 	intensity float32        // Light intensity
-	direction math32.Vector3 // Direction in world coordinates
 	uni       gls.Uniform    // Uniform location cache
 	udata     struct {       // Combined uniform data in 5 vec3:
 		color          math32.Color   // Light color
 		position       math32.Vector3 // Light position
-		direction      math32.Vector3 // Light position
+		direction      math32.Vector3 // Light direction
 		angularDecay   float32        // Angular decay factor
 		cutoffAngle    float32        // Cut off angle
 		linearDecay    float32        // Distance linear decay
@@ -76,18 +75,6 @@ func (l *Spot) Intensity() float32 {
 	return l.intensity
 }
 
-// SetDirection sets the direction of the spot light in world coordinates
-func (l *Spot) SetDirection(direction *math32.Vector3) {
-
-	l.direction = *direction
-}
-
-// Direction returns the current direction of this spot light in world coordinates
-func (l *Spot) Direction(direction *math32.Vector3) math32.Vector3 {
-
-	return l.direction
-}
-
 // SetCutoffAngle sets the cutoff angle in degrees from 0 to 90
 func (l *Spot) SetCutoffAngle(angle float32) {
 
@@ -150,7 +137,9 @@ func (l *Spot) RenderSetup(gs *gls.GLS, rinfo *core.RenderInfo, idx int) {
 	l.udata.position.Z = pos4.Z
 
 	// Calculates and updates light direction uniform in camera coordinates
-	pos4.SetVector3(&l.direction, 0.0)
+	var dir math32.Vector3
+	l.WorldDirection(&dir)
+	pos4.SetVector3(&dir, 0.0)
 	pos4.ApplyMatrix4(&rinfo.ViewMatrix)
 	l.udata.direction.X = pos4.X
 	l.udata.direction.Y = pos4.Y

+ 8 - 2
material/material.go

@@ -325,8 +325,14 @@ func (mat *Material) RenderSetup(gs *gls.GLS) {
 	}
 
 	// Render textures
-	for idx, tex := range mat.textures {
-		tex.RenderSetup(gs, idx)
+	// Keep track of counts of unique sampler names to correctly index sampler arrays
+	samplerCounts := make(map[string]int)
+	for slotIdx, tex := range mat.textures {
+		samplerName, _ := tex.GetUniformNames()
+		uniIdx, _ := samplerCounts[samplerName]
+		log.Error("slotIdx: %v, uniIdx: %v, sampleName: %v", slotIdx, uniIdx, samplerName)
+		tex.RenderSetup(gs, slotIdx, uniIdx)
+		samplerCounts[samplerName] = uniIdx+1
 	}
 }
 

+ 15 - 22
material/physical.go

@@ -42,9 +42,9 @@ func NewPhysical() *Physical {
 	// Creates uniform and set default values
 	m.uni.Init("Material")
 	m.udata.baseColorFactor = math32.Color4{1, 1, 1, 1}
-	m.udata.emissiveFactor = math32.Color4{0, 0, 0, 0}
-	m.udata.metallicFactor = 0.5
-	m.udata.roughnessFactor = 0.5
+	m.udata.emissiveFactor = math32.Color4{1, 1, 1, 1}
+	m.udata.metallicFactor = 1
+	m.udata.roughnessFactor = 1
 	return m
 }
 
@@ -94,8 +94,10 @@ func (m *Physical) SetBaseColorMap(tex *texture.Texture2D) *Physical {
 	if m.baseColorTex != nil {
 		m.baseColorTex.SetUniformNames("uBaseColorSampler", "uBaseColorTexParams")
 		m.SetShaderDefine("HAS_BASECOLORMAP", "")
+		m.AddTexture(m.baseColorTex)
 	} else {
 		m.UnsetShaderDefine("HAS_BASECOLORMAP")
+		m.RemoveTexture(m.baseColorTex)
 	}
 	return m
 }
@@ -108,8 +110,10 @@ func (m *Physical) SetMetallicRoughnessMap(tex *texture.Texture2D) *Physical {
 	if m.metallicRoughnessTex != nil {
 		m.metallicRoughnessTex.SetUniformNames("uMetallicRoughnessSampler", "uMetallicRoughnessTexParams")
 		m.SetShaderDefine("HAS_METALROUGHNESSMAP", "")
+		m.AddTexture(m.metallicRoughnessTex)
 	} else {
 		m.UnsetShaderDefine("HAS_METALROUGHNESSMAP")
+		m.RemoveTexture(m.metallicRoughnessTex)
 	}
 	return m
 }
@@ -121,10 +125,12 @@ func (m *Physical) SetNormalMap(tex *texture.Texture2D) *Physical {
 
 	m.normalTex = tex
 	if m.normalTex != nil {
-		m.normalTex.SetUniformNames("uNormalSampler", "uNormalSamplerTexParams")
+		m.normalTex.SetUniformNames("uNormalSampler", "uNormalTexParams")
 		m.SetShaderDefine("HAS_NORMALMAP", "")
+		m.AddTexture(m.normalTex)
 	} else {
 		m.UnsetShaderDefine("HAS_NORMALMAP")
+		m.RemoveTexture(m.normalTex)
 	}
 	return m
 }
@@ -137,8 +143,10 @@ func (m *Physical) SetOcclusionMap(tex *texture.Texture2D) *Physical {
 	if m.occlusionTex != nil {
 		m.occlusionTex.SetUniformNames("uOcclusionSampler", "uOcclusionTexParams")
 		m.SetShaderDefine("HAS_OCCLUSIONMAP", "")
+		m.AddTexture(m.occlusionTex)
 	} else {
 		m.UnsetShaderDefine("HAS_OCCLUSIONMAP")
+		m.RemoveTexture(m.occlusionTex)
 	}
 	return m
 }
@@ -149,10 +157,12 @@ func (m *Physical) SetEmissiveMap(tex *texture.Texture2D) *Physical {
 
 	m.emissiveTex = tex
 	if m.emissiveTex != nil {
-		m.emissiveTex.SetUniformNames("uEmissiveSampler", "uEmissiveSamplerTexParams")
+		m.emissiveTex.SetUniformNames("uEmissiveSampler", "uEmissiveTexParams")
 		m.SetShaderDefine("HAS_EMISSIVEMAP", "")
+		m.AddTexture(m.emissiveTex)
 	} else {
 		m.UnsetShaderDefine("HAS_EMISSIVEMAP")
+		m.RemoveTexture(m.emissiveTex)
 	}
 	return m
 }
@@ -164,21 +174,4 @@ func (m *Physical) RenderSetup(gl *gls.GLS) {
 	location := m.uni.Location(gl)
 	log.Error("Physical RenderSetup location:%v udata:%+v", location, m.udata)
 	gl.Uniform4fvUP(location, physicalVec4Count, unsafe.Pointer(&m.udata))
-
-	// Transfer optional textures
-	if m.baseColorTex != nil {
-		m.baseColorTex.RenderSetup(gl, 0)
-	}
-	if m.metallicRoughnessTex != nil {
-		m.metallicRoughnessTex.RenderSetup(gl, 0)
-	}
-	if m.normalTex != nil {
-		m.normalTex.RenderSetup(gl, 0)
-	}
-	if m.occlusionTex != nil {
-		m.occlusionTex.RenderSetup(gl, 0)
-	}
-	if m.emissiveTex != nil {
-		m.emissiveTex.RenderSetup(gl, 0)
-	}
 }

+ 1 - 0
renderer/shaders/include/material.glsl

@@ -31,6 +31,7 @@ uniform vec3 Material[6];
 // It should be called for each texture index. It uses two externally defined variables:
 // vec4 texColor
 // vec4 texMixed
+// TODO alpha blending (dont use mix) see panel shader
 #define MIX_TEXTURE(i)                                                                       \
     if (MatTexVisible(i)) {                                                                  \
         texColor = texture(MatTexture[i], FragTexcoord * MatTexRepeat(i) + MatTexOffset(i)); \

+ 229 - 85
renderer/shaders/physical_fragment.glsl

@@ -27,22 +27,21 @@ precision highp float;
 //#endif
 
 #ifdef HAS_BASECOLORMAP
-uniform sampler2D u_BaseColorSampler;
+uniform sampler2D uBaseColorSampler;
+#endif
+#ifdef HAS_METALROUGHNESSMAP
+uniform sampler2D uMetallicRoughnessSampler;
 #endif
 #ifdef HAS_NORMALMAP
-uniform sampler2D u_NormalSampler;
-uniform float u_NormalScale;
+uniform sampler2D uNormalSampler;
+//uniform float uNormalScale;
 #endif
 #ifdef HAS_EMISSIVEMAP
-uniform sampler2D u_EmissiveSampler;
-uniform vec3 u_EmissiveFactor;
-#endif
-#ifdef HAS_METALROUGHNESSMAP
-uniform sampler2D u_MetallicRoughnessSampler;
+uniform sampler2D uEmissiveSampler;
 #endif
 #ifdef HAS_OCCLUSIONMAP
-uniform sampler2D u_OcclusionSampler;
-uniform float u_OcclusionStrength;
+uniform sampler2D uOcclusionSampler;
+uniform float uOcclusionStrength;
 #endif
 
 // Material parameters uniform array
@@ -67,13 +66,17 @@ out vec4 FragColor;
 // Encapsulate the various inputs used by the various functions in the shading equation
 // We store values in this struct to simplify the integration of alternative implementations
 // of the shading terms, outlined in the Readme.MD Appendix.
-struct PBRInfo
+struct PBRLightInfo
 {
     float NdotL;                  // cos angle between normal and light direction
     float NdotV;                  // cos angle between normal and view direction
     float NdotH;                  // cos angle between normal and half vector
     float LdotH;                  // cos angle between light direction and half vector
     float VdotH;                  // cos angle between view direction and half vector
+};
+
+struct PBRInfo
+{
     float perceptualRoughness;    // roughness value, as authored by the model creator (input to shader)
     float metalness;              // metallic value at the surface
     vec3 reflectance0;            // full reflectance color (normal incidence angle)
@@ -91,12 +94,12 @@ vec4 SRGBtoLINEAR(vec4 srgbIn) {
 //    #ifdef SRGB_FAST_APPROXIMATION
 //        vec3 linOut = pow(srgbIn.xyz,vec3(2.2));
 //    #else //SRGB_FAST_APPROXIMATION
-//        vec3 bLess = step(vec3(0.04045),srgbIn.xyz);
-//        vec3 linOut = mix( srgbIn.xyz/vec3(12.92), pow((srgbIn.xyz+vec3(0.055))/vec3(1.055),vec3(2.4)), bLess );
+        vec3 bLess = step(vec3(0.04045),srgbIn.xyz);
+        vec3 linOut = mix( srgbIn.xyz/vec3(12.92), pow((srgbIn.xyz+vec3(0.055))/vec3(1.055),vec3(2.4)), bLess );
 //    #endif //SRGB_FAST_APPROXIMATION
-//        return vec4(linOut,srgbIn.w);
+        return vec4(linOut,srgbIn.w);
 //#else //MANUAL_SRGB
-    return srgbIn;
+//    return srgbIn;
 //#endif //MANUAL_SRGB
 }
 
@@ -112,11 +115,11 @@ vec3 getNormal()
     vec3 tex_dy = dFdy(vec3(FragTexcoord, 0.0));
     vec3 t = (tex_dy.t * pos_dx - tex_dx.t * pos_dy) / (tex_dx.s * tex_dy.t - tex_dy.s * tex_dx.t);
 
-#ifdef HAS_NORMALS
+//#ifdef HAS_NORMALS
     vec3 ng = normalize(Normal);
-#else
-    vec3 ng = cross(pos_dx, pos_dy);
-#endif
+//#else
+//    vec3 ng = cross(pos_dx, pos_dy);
+//#endif
 
     t = normalize(t - ng * dot(ng, t));
     vec3 b = normalize(cross(ng, t));
@@ -126,6 +129,7 @@ vec3 getNormal()
 //#endif
 
 #ifdef HAS_NORMALMAP
+    float uNormalScale = 1.0;
     vec3 n = texture2D(uNormalSampler, FragTexcoord).rgb;
     n = normalize(tbn * ((2.0 * n - 1.0) * vec3(uNormalScale, uNormalScale, 1.0)));
 #else
@@ -136,32 +140,32 @@ vec3 getNormal()
     return n;
 }
 
-//// Calculation of the lighting contribution from an optional Image Based Light source.
-//// Precomputed Environment Maps are required uniform inputs and are computed as outlined in [1].
-//// See our README.md on Environment Maps [3] for additional discussion.
-//vec3 getIBLContribution(PBRInfo pbrInputs, vec3 n, vec3 reflection)
-//{
-//    float mipCount = 9.0; // resolution of 512x512
-//    float lod = (pbrInputs.perceptualRoughness * mipCount);
-//    // retrieve a scale and bias to F0. See [1], Figure 3
-//    vec3 brdf = SRGBtoLINEAR(texture2D(u_brdfLUT, vec2(pbrInputs.NdotV, 1.0 - pbrInputs.perceptualRoughness))).rgb;
-//    vec3 diffuseLight = SRGBtoLINEAR(textureCube(u_DiffuseEnvSampler, n)).rgb;
-//
-////#ifdef USE_TEX_LOD
-////    vec3 specularLight = SRGBtoLINEAR(textureCubeLodEXT(u_SpecularEnvSampler, reflection, lod)).rgb;
-////#else
-//    vec3 specularLight = SRGBtoLINEAR(textureCube(u_SpecularEnvSampler, reflection)).rgb;
-////#endif
-//
-//    vec3 diffuse = diffuseLight * pbrInputs.diffuseColor;
-//    vec3 specular = specularLight * (pbrInputs.specularColor * brdf.x + brdf.y);
-//
-//    // For presentation, this allows us to disable IBL terms
+// Calculation of the lighting contribution from an optional Image Based Light source.
+// Precomputed Environment Maps are required uniform inputs and are computed as outlined in [1].
+// See our README.md on Environment Maps [3] for additional discussion.
+vec3 getIBLContribution(PBRInfo pbrInputs, PBRLightInfo pbrLight, vec3 n, vec3 reflection)
+{
+    float mipCount = 9.0; // resolution of 512x512
+    float lod = (pbrInputs.perceptualRoughness * mipCount);
+    // retrieve a scale and bias to F0. See [1], Figure 3
+    vec3 brdf = vec3(0.5,0.5,0.5);//SRGBtoLINEAR(texture2D(u_brdfLUT, vec2(pbrLight.NdotV, 1.0 - pbrInputs.perceptualRoughness))).rgb;
+    vec3 diffuseLight = vec3(0.5,0.5,0.5);//SRGBtoLINEAR(textureCube(u_DiffuseEnvSampler, n)).rgb;
+
+//#ifdef USE_TEX_LOD
+//    vec3 specularLight = SRGBtoLINEAR(textureCubeLodEXT(u_SpecularEnvSampler, reflection, lod)).rgb;
+//#else
+    vec3 specularLight = vec3(0.5,0.5,0.5);//SRGBtoLINEAR(textureCube(u_SpecularEnvSampler, reflection)).rgb;
+//#endif
+
+    vec3 diffuse = diffuseLight * pbrInputs.diffuseColor;
+    vec3 specular = specularLight * (pbrInputs.specularColor * brdf.x + brdf.y);
+
+    // For presentation, this allows us to disable IBL terms
 //    diffuse *= u_ScaleIBLAmbient.x;
 //    specular *= u_ScaleIBLAmbient.y;
-//
-//    return diffuse + specular;
-//}
+
+    return diffuse + specular;
+}
 
 // Basic Lambertian diffuse
 // Implementation from Lambert's Photometria https://archive.org/details/lambertsphotome00lambgoog
@@ -173,19 +177,19 @@ vec3 diffuse(PBRInfo pbrInputs)
 
 // The following equation models the Fresnel reflectance term of the spec equation (aka F())
 // Implementation of fresnel from [4], Equation 15
-vec3 specularReflection(PBRInfo pbrInputs)
+vec3 specularReflection(PBRInfo pbrInputs, PBRLightInfo pbrLight)
 {
-    return pbrInputs.reflectance0 + (pbrInputs.reflectance90 - pbrInputs.reflectance0) * pow(clamp(1.0 - pbrInputs.VdotH, 0.0, 1.0), 5.0);
+    return pbrInputs.reflectance0 + (pbrInputs.reflectance90 - pbrInputs.reflectance0) * pow(clamp(1.0 - pbrLight.VdotH, 0.0, 1.0), 5.0);
 }
 
 // This calculates the specular geometric attenuation (aka G()),
 // where rougher material will reflect less light back to the viewer.
 // This implementation is based on [1] Equation 4, and we adopt their modifications to
 // alphaRoughness as input as originally proposed in [2].
-float geometricOcclusion(PBRInfo pbrInputs)
+float geometricOcclusion(PBRInfo pbrInputs, PBRLightInfo pbrLight)
 {
-    float NdotL = pbrInputs.NdotL;
-    float NdotV = pbrInputs.NdotV;
+    float NdotL = pbrLight.NdotL;
+    float NdotV = pbrLight.NdotV;
     float r = pbrInputs.alphaRoughness;
 
     float attenuationL = 2.0 * NdotL / (NdotL + sqrt(r * r + (1.0 - r * r) * (NdotL * NdotL)));
@@ -196,13 +200,52 @@ float geometricOcclusion(PBRInfo pbrInputs)
 // The following equation(s) model the distribution of microfacet normals across the area being drawn (aka D())
 // Implementation from "Average Irregularity Representation of a Roughened Surface for Ray Reflection" by T. S. Trowbridge, and K. P. Reitz
 // Follows the distribution function recommended in the SIGGRAPH 2013 course notes from EPIC Games [1], Equation 3.
-float microfacetDistribution(PBRInfo pbrInputs)
+float microfacetDistribution(PBRInfo pbrInputs, PBRLightInfo pbrLight)
 {
     float roughnessSq = pbrInputs.alphaRoughness * pbrInputs.alphaRoughness;
-    float f = (pbrInputs.NdotH * roughnessSq - pbrInputs.NdotH) * pbrInputs.NdotH + 1.0;
+    float f = (pbrLight.NdotH * roughnessSq - pbrLight.NdotH) * pbrLight.NdotH + 1.0;
     return roughnessSq / (M_PI * f * f);
 }
 
+vec3 pbrModel(PBRInfo pbrInputs, vec3 lightColor, vec3 lightDir) {
+
+//    vec3 lightDir = lightPos - vec3(Position);
+
+    vec3 n = getNormal();                             // normal at surface point
+    vec3 v = normalize(CamDir);                       // Vector from surface point to camera
+    vec3 l = normalize(lightDir);                     // Vector from surface point to light
+    vec3 h = normalize(l+v);                          // Half vector between both l and v
+    vec3 reflection = -normalize(reflect(v, n));
+
+    float NdotL = clamp(dot(n, l), 0.001, 1.0);
+    float NdotV = abs(dot(n, v)) + 0.001;
+    float NdotH = clamp(dot(n, h), 0.0, 1.0);
+    float LdotH = clamp(dot(l, h), 0.0, 1.0);
+    float VdotH = clamp(dot(v, h), 0.0, 1.0);
+
+    PBRLightInfo pbrLight = PBRLightInfo(
+        NdotL,
+        NdotV,
+        NdotH,
+        LdotH,
+        VdotH
+    );
+
+    // Calculate the shading terms for the microfacet specular shading model
+    vec3 F = specularReflection(pbrInputs, pbrLight);
+    float G = geometricOcclusion(pbrInputs, pbrLight);
+    float D = microfacetDistribution(pbrInputs, pbrLight);
+
+    // Calculation of analytical lighting contribution
+    vec3 diffuseContrib = (1.0 - F) * diffuse(pbrInputs);
+    vec3 specContrib = F * G * D / (4.0 * NdotL * NdotV);
+    // Obtain final intensity as reflectance (BRDF) scaled by the energy of the light (cosine law)
+    vec3 color = NdotL * lightColor * (diffuseContrib + specContrib);
+
+    return color;
+}
+
+
 void main() {
 
     float perceptualRoughness = uRoughnessFactor;
@@ -232,39 +275,24 @@ void main() {
     vec3 f0 = vec3(0.04);
     vec3 diffuseColor = baseColor.rgb * (vec3(1.0) - f0);
     diffuseColor *= 1.0 - metallic;
+
+//    vec3 AmbientLight = vec3(0.5);
+//    diffuseColor.rgb += AmbientLight;
+//    diffuseColor *= baseColor.rgb;
+//    diffuseColor = max(diffuseColor, 0.0);
+
     vec3 specularColor = mix(f0, baseColor.rgb, uMetallicFactor);
 
     // Compute reflectance.
     float reflectance = max(max(specularColor.r, specularColor.g), specularColor.b);
 
     // For typical incident reflectance range (between 4% to 100%) set the grazing reflectance to 100% for typical fresnel effect.
-    // For very low reflectance range on highly diffuse objects (below 4%), incrementally reduce grazing reflecance to 0%.
+    // For very low reflectance range on highly diffuse objects (below 4%), incrementally reduce grazing reflectance to 0%.
     float reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0);
     vec3 specularEnvironmentR0 = specularColor.rgb;
     vec3 specularEnvironmentR90 = vec3(1.0, 1.0, 1.0) * reflectance90;
 
-    // TODO These are not currently uniforms - and need to support all kinds of lights!
-    vec3 u_LightColor = vec3(1,1,1);
-    vec3 u_LightDirection = vec3(1,0,0);
-
-    vec3 n = getNormal();                             // normal at surface point
-    vec3 v = normalize(CamDir);                       // Vector from surface point to camera
-    vec3 l = normalize(u_LightDirection);             // Vector from surface point to light
-    vec3 h = normalize(l+v);                          // Half vector between both l and v
-    vec3 reflection = -normalize(reflect(v, n));
-
-    float NdotL = clamp(dot(n, l), 0.001, 1.0);
-    float NdotV = abs(dot(n, v)) + 0.001;
-    float NdotH = clamp(dot(n, h), 0.0, 1.0);
-    float LdotH = clamp(dot(l, h), 0.0, 1.0);
-    float VdotH = clamp(dot(v, h), 0.0, 1.0);
-
     PBRInfo pbrInputs = PBRInfo(
-        NdotL,
-        NdotV,
-        NdotH,
-        LdotH,
-        VdotH,
         perceptualRoughness,
         metallic,
         specularEnvironmentR0,
@@ -274,16 +302,103 @@ void main() {
         specularColor
     );
 
-    // Calculate the shading terms for the microfacet specular shading model
-    vec3 F = specularReflection(pbrInputs);
-    float G = geometricOcclusion(pbrInputs);
-    float D = microfacetDistribution(pbrInputs);
+//    vec3 normal = getNormal();
+    vec3 color = vec3(0.0);
 
-    // Calculation of analytical lighting contribution
-    vec3 diffuseContrib = (1.0 - F) * diffuse(pbrInputs);
-    vec3 specContrib = F * G * D / (4.0 * NdotL * NdotV);
-    // Obtain final intensity as reflectance (BRDF) scaled by the energy of the light (cosine law)
-    vec3 color = NdotL * u_LightColor * (diffuseContrib + specContrib);
+#if AMB_LIGHTS>0
+    // Ambient lights
+    for (int i = 0; i < AMB_LIGHTS; i++) {
+        color += AmbientLightColor[i] * pbrInputs.diffuseColor;
+    }
+#endif
+
+#if DIR_LIGHTS>0
+    // Directional lights
+    for (int i = 0; i < DIR_LIGHTS; i++) {
+        // Diffuse reflection
+        // DirLightPosition is the direction of the current light
+        vec3 lightDirection = normalize(DirLightPosition(i));
+        // PBR
+        color += pbrModel(pbrInputs, DirLightColor(i), lightDirection);
+    }
+#endif
+
+#if POINT_LIGHTS>0
+    // Point lights
+    for (int i = 0; i < POINT_LIGHTS; i++) {
+        // Common calculations
+        // Calculates the direction and distance from the current vertex to this point light.
+        vec3 lightDirection = PointLightPosition(i) - vec3(Position);
+        float lightDistance = length(lightDirection);
+        // Normalizes the lightDirection
+        lightDirection = lightDirection / lightDistance;
+        // Calculates the attenuation due to the distance of the light
+        float attenuation = 1.0 / (1.0 + PointLightLinearDecay(i) * lightDistance +
+            PointLightQuadraticDecay(i) * lightDistance * lightDistance);
+        vec3 attenuatedColor = PointLightColor(i) * attenuation;
+        // PBR
+        color += pbrModel(pbrInputs, attenuatedColor, lightDirection);
+    }
+#endif
+
+#if SPOT_LIGHTS>0
+//    for (int i = 0; i < SPOT_LIGHTS; i++) {
+//        // Calculates the direction and distance from the current vertex to this spot light.
+//        vec3 lightDirection = SpotLightPosition(i) - vec3(Position);
+//        float lightDistance = length(lightDirection);
+//        lightDirection = lightDirection / lightDistance;
+//
+//        // Calculates the attenuation due to the distance of the light
+//        float attenuation = 1.0 / (1.0 + SpotLightLinearDecay(i) * lightDistance +
+//            SpotLightQuadraticDecay(i) * lightDistance * lightDistance);
+//
+//        // Calculates the angle between the vertex direction and spot direction
+//        // If this angle is greater than the cutoff the spotlight will not contribute
+//        // to the final color.
+//        float angle = acos(dot(-lightDirection, SpotLightDirection(i)));
+//        float cutoff = radians(clamp(SpotLightCutoffAngle(i), 0.0, 90.0));
+//
+//        if (angle < cutoff) {
+//            float spotFactor = pow(dot(-lightDirection, SpotLightDirection(i)), SpotLightAngularDecay(i));
+//
+//            // Diffuse reflection
+//            float dotNormal = max(dot(lightDirection, normal), 0.0);
+//            color += SpotLightColor(i) * dotNormal * attenuation * spotFactor;
+//
+//            // Specular reflection
+//            vec3 ref = reflect(-lightDirection, normal);
+//            if (dotNormal > 0.0) {
+//                color += SpotLightColor(i) * pow(max(dot(ref, CamDir), 0.0), 5) * attenuation * spotFactor;
+//            }
+//        }
+//    }
+
+// TODO
+    for (int i = 0; i < SPOT_LIGHTS; i++) {
+
+        // Calculates the direction and distance from the current vertex to this spot light.
+        vec3 lightDirection = SpotLightPosition(i) - vec3(Position);
+        float lightDistance = length(lightDirection);
+        lightDirection = lightDirection / lightDistance;
+
+        // Calculates the attenuation due to the distance of the light
+        float attenuation = 1.0 / (1.0 + SpotLightLinearDecay(i) * lightDistance +
+            SpotLightQuadraticDecay(i) * lightDistance * lightDistance);
+
+        // Calculates the angle between the vertex direction and spot direction
+        // If this angle is greater than the cutoff the spotlight will not contribute
+        // to the final color.
+        float angle = acos(dot(-lightDirection, SpotLightDirection(i)));
+        float cutoff = radians(clamp(SpotLightCutoffAngle(i), 0.0, 90.0));
+
+        if (angle < cutoff) {
+            float spotFactor = pow(dot(-lightDirection, SpotLightDirection(i)), SpotLightAngularDecay(i));
+            vec3 attenuatedColor = SpotLightColor(i) * attenuation * spotFactor;
+            // PBR
+            color += pbrModel(pbrInputs, attenuatedColor, lightDirection);
+        }
+    }
+#endif
 
     // Calculate lighting contribution from image based lighting source (IBL)
 //#ifdef USE_IBL
@@ -292,18 +407,47 @@ void main() {
 
     // Apply optional PBR terms for additional (optional) shading
 #ifdef HAS_OCCLUSIONMAP
-    float ao = texture2D(uOcclusionSampler, v_UV).r;
-    color = mix(color, color * ao, u_OcclusionStrength);
+    float ao = texture2D(uOcclusionSampler, FragTexcoord).r;
+    color = mix(color, color * ao, 1.0);//, uOcclusionStrength);
 #endif
 
 #ifdef HAS_EMISSIVEMAP
-    vec3 emissive = SRGBtoLINEAR(texture2D(u_EmissiveSampler, v_UV)).rgb * u_EmissiveFactor;
+    vec3 emissive = SRGBtoLINEAR(texture2D(uEmissiveSampler, FragTexcoord)).rgb * vec3(uEmissiveColor);
     color += emissive;
 #endif
 
+    // Base Color
+//    FragColor = baseColor;
+
+    // Normal
+//    FragColor = vec4(n, 1.0);
+
+    // Emissive Color
+//    FragColor = vec4(emissive, 1.0);
+
+    // F
+//    color = F;
+
+    // G
+//    color = vec3(G);
+
+    // D
+//    color = vec3(D);
+
+    // Specular
+//    color = specContrib;
+
+    // Diffuse
+//    color = diffuseContrib;
+
+    // Roughness
+//    color = vec3(perceptualRoughness);
+
+    // Metallic
+//    color = vec3(metallic);
+
     // Final fragment color
     FragColor = vec4(pow(color,vec3(1.0/2.2)), baseColor.a);
-
 }
 
 

+ 230 - 85
renderer/shaders/sources.go

@@ -93,6 +93,7 @@ uniform vec3 Material[6];
 // It should be called for each texture index. It uses two externally defined variables:
 // vec4 texColor
 // vec4 texMixed
+// TODO alpha blending (dont use mix) see panel shader
 #define MIX_TEXTURE(i)                                                                       \
     if (MatTexVisible(i)) {                                                                  \
         texColor = texture(MatTexture[i], FragTexcoord * MatTexRepeat(i) + MatTexOffset(i)); \
@@ -536,22 +537,21 @@ precision highp float;
 //#endif
 
 #ifdef HAS_BASECOLORMAP
-uniform sampler2D u_BaseColorSampler;
+uniform sampler2D uBaseColorSampler;
+#endif
+#ifdef HAS_METALROUGHNESSMAP
+uniform sampler2D uMetallicRoughnessSampler;
 #endif
 #ifdef HAS_NORMALMAP
-uniform sampler2D u_NormalSampler;
-uniform float u_NormalScale;
+uniform sampler2D uNormalSampler;
+//uniform float uNormalScale;
 #endif
 #ifdef HAS_EMISSIVEMAP
-uniform sampler2D u_EmissiveSampler;
-uniform vec3 u_EmissiveFactor;
-#endif
-#ifdef HAS_METALROUGHNESSMAP
-uniform sampler2D u_MetallicRoughnessSampler;
+uniform sampler2D uEmissiveSampler;
 #endif
 #ifdef HAS_OCCLUSIONMAP
-uniform sampler2D u_OcclusionSampler;
-uniform float u_OcclusionStrength;
+uniform sampler2D uOcclusionSampler;
+uniform float uOcclusionStrength;
 #endif
 
 // Material parameters uniform array
@@ -576,13 +576,17 @@ out vec4 FragColor;
 // Encapsulate the various inputs used by the various functions in the shading equation
 // We store values in this struct to simplify the integration of alternative implementations
 // of the shading terms, outlined in the Readme.MD Appendix.
-struct PBRInfo
+struct PBRLightInfo
 {
     float NdotL;                  // cos angle between normal and light direction
     float NdotV;                  // cos angle between normal and view direction
     float NdotH;                  // cos angle between normal and half vector
     float LdotH;                  // cos angle between light direction and half vector
     float VdotH;                  // cos angle between view direction and half vector
+};
+
+struct PBRInfo
+{
     float perceptualRoughness;    // roughness value, as authored by the model creator (input to shader)
     float metalness;              // metallic value at the surface
     vec3 reflectance0;            // full reflectance color (normal incidence angle)
@@ -600,12 +604,12 @@ vec4 SRGBtoLINEAR(vec4 srgbIn) {
 //    #ifdef SRGB_FAST_APPROXIMATION
 //        vec3 linOut = pow(srgbIn.xyz,vec3(2.2));
 //    #else //SRGB_FAST_APPROXIMATION
-//        vec3 bLess = step(vec3(0.04045),srgbIn.xyz);
-//        vec3 linOut = mix( srgbIn.xyz/vec3(12.92), pow((srgbIn.xyz+vec3(0.055))/vec3(1.055),vec3(2.4)), bLess );
+        vec3 bLess = step(vec3(0.04045),srgbIn.xyz);
+        vec3 linOut = mix( srgbIn.xyz/vec3(12.92), pow((srgbIn.xyz+vec3(0.055))/vec3(1.055),vec3(2.4)), bLess );
 //    #endif //SRGB_FAST_APPROXIMATION
-//        return vec4(linOut,srgbIn.w);
+        return vec4(linOut,srgbIn.w);
 //#else //MANUAL_SRGB
-    return srgbIn;
+//    return srgbIn;
 //#endif //MANUAL_SRGB
 }
 
@@ -621,11 +625,11 @@ vec3 getNormal()
     vec3 tex_dy = dFdy(vec3(FragTexcoord, 0.0));
     vec3 t = (tex_dy.t * pos_dx - tex_dx.t * pos_dy) / (tex_dx.s * tex_dy.t - tex_dy.s * tex_dx.t);
 
-#ifdef HAS_NORMALS
+//#ifdef HAS_NORMALS
     vec3 ng = normalize(Normal);
-#else
-    vec3 ng = cross(pos_dx, pos_dy);
-#endif
+//#else
+//    vec3 ng = cross(pos_dx, pos_dy);
+//#endif
 
     t = normalize(t - ng * dot(ng, t));
     vec3 b = normalize(cross(ng, t));
@@ -635,6 +639,7 @@ vec3 getNormal()
 //#endif
 
 #ifdef HAS_NORMALMAP
+    float uNormalScale = 1.0;
     vec3 n = texture2D(uNormalSampler, FragTexcoord).rgb;
     n = normalize(tbn * ((2.0 * n - 1.0) * vec3(uNormalScale, uNormalScale, 1.0)));
 #else
@@ -645,32 +650,32 @@ vec3 getNormal()
     return n;
 }
 
-//// Calculation of the lighting contribution from an optional Image Based Light source.
-//// Precomputed Environment Maps are required uniform inputs and are computed as outlined in [1].
-//// See our README.md on Environment Maps [3] for additional discussion.
-//vec3 getIBLContribution(PBRInfo pbrInputs, vec3 n, vec3 reflection)
-//{
-//    float mipCount = 9.0; // resolution of 512x512
-//    float lod = (pbrInputs.perceptualRoughness * mipCount);
-//    // retrieve a scale and bias to F0. See [1], Figure 3
-//    vec3 brdf = SRGBtoLINEAR(texture2D(u_brdfLUT, vec2(pbrInputs.NdotV, 1.0 - pbrInputs.perceptualRoughness))).rgb;
-//    vec3 diffuseLight = SRGBtoLINEAR(textureCube(u_DiffuseEnvSampler, n)).rgb;
-//
-////#ifdef USE_TEX_LOD
-////    vec3 specularLight = SRGBtoLINEAR(textureCubeLodEXT(u_SpecularEnvSampler, reflection, lod)).rgb;
-////#else
-//    vec3 specularLight = SRGBtoLINEAR(textureCube(u_SpecularEnvSampler, reflection)).rgb;
-////#endif
-//
-//    vec3 diffuse = diffuseLight * pbrInputs.diffuseColor;
-//    vec3 specular = specularLight * (pbrInputs.specularColor * brdf.x + brdf.y);
-//
-//    // For presentation, this allows us to disable IBL terms
+// Calculation of the lighting contribution from an optional Image Based Light source.
+// Precomputed Environment Maps are required uniform inputs and are computed as outlined in [1].
+// See our README.md on Environment Maps [3] for additional discussion.
+vec3 getIBLContribution(PBRInfo pbrInputs, PBRLightInfo pbrLight, vec3 n, vec3 reflection)
+{
+    float mipCount = 9.0; // resolution of 512x512
+    float lod = (pbrInputs.perceptualRoughness * mipCount);
+    // retrieve a scale and bias to F0. See [1], Figure 3
+    vec3 brdf = vec3(0.5,0.5,0.5);//SRGBtoLINEAR(texture2D(u_brdfLUT, vec2(pbrLight.NdotV, 1.0 - pbrInputs.perceptualRoughness))).rgb;
+    vec3 diffuseLight = vec3(0.5,0.5,0.5);//SRGBtoLINEAR(textureCube(u_DiffuseEnvSampler, n)).rgb;
+
+//#ifdef USE_TEX_LOD
+//    vec3 specularLight = SRGBtoLINEAR(textureCubeLodEXT(u_SpecularEnvSampler, reflection, lod)).rgb;
+//#else
+    vec3 specularLight = vec3(0.5,0.5,0.5);//SRGBtoLINEAR(textureCube(u_SpecularEnvSampler, reflection)).rgb;
+//#endif
+
+    vec3 diffuse = diffuseLight * pbrInputs.diffuseColor;
+    vec3 specular = specularLight * (pbrInputs.specularColor * brdf.x + brdf.y);
+
+    // For presentation, this allows us to disable IBL terms
 //    diffuse *= u_ScaleIBLAmbient.x;
 //    specular *= u_ScaleIBLAmbient.y;
-//
-//    return diffuse + specular;
-//}
+
+    return diffuse + specular;
+}
 
 // Basic Lambertian diffuse
 // Implementation from Lambert's Photometria https://archive.org/details/lambertsphotome00lambgoog
@@ -682,19 +687,19 @@ vec3 diffuse(PBRInfo pbrInputs)
 
 // The following equation models the Fresnel reflectance term of the spec equation (aka F())
 // Implementation of fresnel from [4], Equation 15
-vec3 specularReflection(PBRInfo pbrInputs)
+vec3 specularReflection(PBRInfo pbrInputs, PBRLightInfo pbrLight)
 {
-    return pbrInputs.reflectance0 + (pbrInputs.reflectance90 - pbrInputs.reflectance0) * pow(clamp(1.0 - pbrInputs.VdotH, 0.0, 1.0), 5.0);
+    return pbrInputs.reflectance0 + (pbrInputs.reflectance90 - pbrInputs.reflectance0) * pow(clamp(1.0 - pbrLight.VdotH, 0.0, 1.0), 5.0);
 }
 
 // This calculates the specular geometric attenuation (aka G()),
 // where rougher material will reflect less light back to the viewer.
 // This implementation is based on [1] Equation 4, and we adopt their modifications to
 // alphaRoughness as input as originally proposed in [2].
-float geometricOcclusion(PBRInfo pbrInputs)
+float geometricOcclusion(PBRInfo pbrInputs, PBRLightInfo pbrLight)
 {
-    float NdotL = pbrInputs.NdotL;
-    float NdotV = pbrInputs.NdotV;
+    float NdotL = pbrLight.NdotL;
+    float NdotV = pbrLight.NdotV;
     float r = pbrInputs.alphaRoughness;
 
     float attenuationL = 2.0 * NdotL / (NdotL + sqrt(r * r + (1.0 - r * r) * (NdotL * NdotL)));
@@ -705,13 +710,52 @@ float geometricOcclusion(PBRInfo pbrInputs)
 // The following equation(s) model the distribution of microfacet normals across the area being drawn (aka D())
 // Implementation from "Average Irregularity Representation of a Roughened Surface for Ray Reflection" by T. S. Trowbridge, and K. P. Reitz
 // Follows the distribution function recommended in the SIGGRAPH 2013 course notes from EPIC Games [1], Equation 3.
-float microfacetDistribution(PBRInfo pbrInputs)
+float microfacetDistribution(PBRInfo pbrInputs, PBRLightInfo pbrLight)
 {
     float roughnessSq = pbrInputs.alphaRoughness * pbrInputs.alphaRoughness;
-    float f = (pbrInputs.NdotH * roughnessSq - pbrInputs.NdotH) * pbrInputs.NdotH + 1.0;
+    float f = (pbrLight.NdotH * roughnessSq - pbrLight.NdotH) * pbrLight.NdotH + 1.0;
     return roughnessSq / (M_PI * f * f);
 }
 
+vec3 pbrModel(PBRInfo pbrInputs, vec3 lightColor, vec3 lightDir) {
+
+//    vec3 lightDir = lightPos - vec3(Position);
+
+    vec3 n = getNormal();                             // normal at surface point
+    vec3 v = normalize(CamDir);                       // Vector from surface point to camera
+    vec3 l = normalize(lightDir);                     // Vector from surface point to light
+    vec3 h = normalize(l+v);                          // Half vector between both l and v
+    vec3 reflection = -normalize(reflect(v, n));
+
+    float NdotL = clamp(dot(n, l), 0.001, 1.0);
+    float NdotV = abs(dot(n, v)) + 0.001;
+    float NdotH = clamp(dot(n, h), 0.0, 1.0);
+    float LdotH = clamp(dot(l, h), 0.0, 1.0);
+    float VdotH = clamp(dot(v, h), 0.0, 1.0);
+
+    PBRLightInfo pbrLight = PBRLightInfo(
+        NdotL,
+        NdotV,
+        NdotH,
+        LdotH,
+        VdotH
+    );
+
+    // Calculate the shading terms for the microfacet specular shading model
+    vec3 F = specularReflection(pbrInputs, pbrLight);
+    float G = geometricOcclusion(pbrInputs, pbrLight);
+    float D = microfacetDistribution(pbrInputs, pbrLight);
+
+    // Calculation of analytical lighting contribution
+    vec3 diffuseContrib = (1.0 - F) * diffuse(pbrInputs);
+    vec3 specContrib = F * G * D / (4.0 * NdotL * NdotV);
+    // Obtain final intensity as reflectance (BRDF) scaled by the energy of the light (cosine law)
+    vec3 color = NdotL * lightColor * (diffuseContrib + specContrib);
+
+    return color;
+}
+
+
 void main() {
 
     float perceptualRoughness = uRoughnessFactor;
@@ -741,39 +785,24 @@ void main() {
     vec3 f0 = vec3(0.04);
     vec3 diffuseColor = baseColor.rgb * (vec3(1.0) - f0);
     diffuseColor *= 1.0 - metallic;
+
+//    vec3 AmbientLight = vec3(0.5);
+//    diffuseColor.rgb += AmbientLight;
+//    diffuseColor *= baseColor.rgb;
+//    diffuseColor = max(diffuseColor, 0.0);
+
     vec3 specularColor = mix(f0, baseColor.rgb, uMetallicFactor);
 
     // Compute reflectance.
     float reflectance = max(max(specularColor.r, specularColor.g), specularColor.b);
 
     // For typical incident reflectance range (between 4% to 100%) set the grazing reflectance to 100% for typical fresnel effect.
-    // For very low reflectance range on highly diffuse objects (below 4%), incrementally reduce grazing reflecance to 0%.
+    // For very low reflectance range on highly diffuse objects (below 4%), incrementally reduce grazing reflectance to 0%.
     float reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0);
     vec3 specularEnvironmentR0 = specularColor.rgb;
     vec3 specularEnvironmentR90 = vec3(1.0, 1.0, 1.0) * reflectance90;
 
-    // TODO These are not currently uniforms - and need to support all kinds of lights!
-    vec3 u_LightColor = vec3(1,1,1);
-    vec3 u_LightDirection = vec3(1,0,0);
-
-    vec3 n = getNormal();                             // normal at surface point
-    vec3 v = normalize(CamDir);                       // Vector from surface point to camera
-    vec3 l = normalize(u_LightDirection);             // Vector from surface point to light
-    vec3 h = normalize(l+v);                          // Half vector between both l and v
-    vec3 reflection = -normalize(reflect(v, n));
-
-    float NdotL = clamp(dot(n, l), 0.001, 1.0);
-    float NdotV = abs(dot(n, v)) + 0.001;
-    float NdotH = clamp(dot(n, h), 0.0, 1.0);
-    float LdotH = clamp(dot(l, h), 0.0, 1.0);
-    float VdotH = clamp(dot(v, h), 0.0, 1.0);
-
     PBRInfo pbrInputs = PBRInfo(
-        NdotL,
-        NdotV,
-        NdotH,
-        LdotH,
-        VdotH,
         perceptualRoughness,
         metallic,
         specularEnvironmentR0,
@@ -783,16 +812,103 @@ void main() {
         specularColor
     );
 
-    // Calculate the shading terms for the microfacet specular shading model
-    vec3 F = specularReflection(pbrInputs);
-    float G = geometricOcclusion(pbrInputs);
-    float D = microfacetDistribution(pbrInputs);
+//    vec3 normal = getNormal();
+    vec3 color = vec3(0.0);
 
-    // Calculation of analytical lighting contribution
-    vec3 diffuseContrib = (1.0 - F) * diffuse(pbrInputs);
-    vec3 specContrib = F * G * D / (4.0 * NdotL * NdotV);
-    // Obtain final intensity as reflectance (BRDF) scaled by the energy of the light (cosine law)
-    vec3 color = NdotL * u_LightColor * (diffuseContrib + specContrib);
+#if AMB_LIGHTS>0
+    // Ambient lights
+    for (int i = 0; i < AMB_LIGHTS; i++) {
+        color += AmbientLightColor[i] * pbrInputs.diffuseColor;
+    }
+#endif
+
+#if DIR_LIGHTS>0
+    // Directional lights
+    for (int i = 0; i < DIR_LIGHTS; i++) {
+        // Diffuse reflection
+        // DirLightPosition is the direction of the current light
+        vec3 lightDirection = normalize(DirLightPosition(i));
+        // PBR
+        color += pbrModel(pbrInputs, DirLightColor(i), lightDirection);
+    }
+#endif
+
+#if POINT_LIGHTS>0
+    // Point lights
+    for (int i = 0; i < POINT_LIGHTS; i++) {
+        // Common calculations
+        // Calculates the direction and distance from the current vertex to this point light.
+        vec3 lightDirection = PointLightPosition(i) - vec3(Position);
+        float lightDistance = length(lightDirection);
+        // Normalizes the lightDirection
+        lightDirection = lightDirection / lightDistance;
+        // Calculates the attenuation due to the distance of the light
+        float attenuation = 1.0 / (1.0 + PointLightLinearDecay(i) * lightDistance +
+            PointLightQuadraticDecay(i) * lightDistance * lightDistance);
+        vec3 attenuatedColor = PointLightColor(i) * attenuation;
+        // PBR
+        color += pbrModel(pbrInputs, attenuatedColor, lightDirection);
+    }
+#endif
+
+#if SPOT_LIGHTS>0
+//    for (int i = 0; i < SPOT_LIGHTS; i++) {
+//        // Calculates the direction and distance from the current vertex to this spot light.
+//        vec3 lightDirection = SpotLightPosition(i) - vec3(Position);
+//        float lightDistance = length(lightDirection);
+//        lightDirection = lightDirection / lightDistance;
+//
+//        // Calculates the attenuation due to the distance of the light
+//        float attenuation = 1.0 / (1.0 + SpotLightLinearDecay(i) * lightDistance +
+//            SpotLightQuadraticDecay(i) * lightDistance * lightDistance);
+//
+//        // Calculates the angle between the vertex direction and spot direction
+//        // If this angle is greater than the cutoff the spotlight will not contribute
+//        // to the final color.
+//        float angle = acos(dot(-lightDirection, SpotLightDirection(i)));
+//        float cutoff = radians(clamp(SpotLightCutoffAngle(i), 0.0, 90.0));
+//
+//        if (angle < cutoff) {
+//            float spotFactor = pow(dot(-lightDirection, SpotLightDirection(i)), SpotLightAngularDecay(i));
+//
+//            // Diffuse reflection
+//            float dotNormal = max(dot(lightDirection, normal), 0.0);
+//            color += SpotLightColor(i) * dotNormal * attenuation * spotFactor;
+//
+//            // Specular reflection
+//            vec3 ref = reflect(-lightDirection, normal);
+//            if (dotNormal > 0.0) {
+//                color += SpotLightColor(i) * pow(max(dot(ref, CamDir), 0.0), 5) * attenuation * spotFactor;
+//            }
+//        }
+//    }
+
+// TODO
+    for (int i = 0; i < SPOT_LIGHTS; i++) {
+
+        // Calculates the direction and distance from the current vertex to this spot light.
+        vec3 lightDirection = SpotLightPosition(i) - vec3(Position);
+        float lightDistance = length(lightDirection);
+        lightDirection = lightDirection / lightDistance;
+
+        // Calculates the attenuation due to the distance of the light
+        float attenuation = 1.0 / (1.0 + SpotLightLinearDecay(i) * lightDistance +
+            SpotLightQuadraticDecay(i) * lightDistance * lightDistance);
+
+        // Calculates the angle between the vertex direction and spot direction
+        // If this angle is greater than the cutoff the spotlight will not contribute
+        // to the final color.
+        float angle = acos(dot(-lightDirection, SpotLightDirection(i)));
+        float cutoff = radians(clamp(SpotLightCutoffAngle(i), 0.0, 90.0));
+
+        if (angle < cutoff) {
+            float spotFactor = pow(dot(-lightDirection, SpotLightDirection(i)), SpotLightAngularDecay(i));
+            vec3 attenuatedColor = SpotLightColor(i) * attenuation * spotFactor;
+            // PBR
+            color += pbrModel(pbrInputs, attenuatedColor, lightDirection);
+        }
+    }
+#endif
 
     // Calculate lighting contribution from image based lighting source (IBL)
 //#ifdef USE_IBL
@@ -801,18 +917,47 @@ void main() {
 
     // Apply optional PBR terms for additional (optional) shading
 #ifdef HAS_OCCLUSIONMAP
-    float ao = texture2D(uOcclusionSampler, v_UV).r;
-    color = mix(color, color * ao, u_OcclusionStrength);
+    float ao = texture2D(uOcclusionSampler, FragTexcoord).r;
+    color = mix(color, color * ao, 1.0);//, uOcclusionStrength);
 #endif
 
 #ifdef HAS_EMISSIVEMAP
-    vec3 emissive = SRGBtoLINEAR(texture2D(u_EmissiveSampler, v_UV)).rgb * u_EmissiveFactor;
+    vec3 emissive = SRGBtoLINEAR(texture2D(uEmissiveSampler, FragTexcoord)).rgb * vec3(uEmissiveColor);
     color += emissive;
 #endif
 
+    // Base Color
+//    FragColor = baseColor;
+
+    // Normal
+//    FragColor = vec4(n, 1.0);
+
+    // Emissive Color
+//    FragColor = vec4(emissive, 1.0);
+
+    // F
+//    color = F;
+
+    // G
+//    color = vec3(G);
+
+    // D
+//    color = vec3(D);
+
+    // Specular
+//    color = specContrib;
+
+    // Diffuse
+//    color = diffuseContrib;
+
+    // Roughness
+//    color = vec3(perceptualRoughness);
+
+    // Metallic
+//    color = vec3(metallic);
+
     // Final fragment color
     FragColor = vec4(pow(color,vec3(1.0/2.2)), baseColor.a);
-
 }
 
 

+ 19 - 11
texture/texture2D.go

@@ -135,6 +135,12 @@ func (t *Texture2D) SetUniformNames(sampler, info string) {
 	t.uniInfo.Init(info)
 }
 
+// GetUniformNames returns the names of the uniforms in the shader for sampler and texture info.
+func (t *Texture2D) GetUniformNames() (sampler, info string) {
+
+	return t.uniUnit.Name(), t.uniInfo.Name()
+}
+
 // SetImage sets a new image for this texture
 func (t *Texture2D) SetImage(imgfile string) error {
 
@@ -299,7 +305,7 @@ func DecodeImage(imgfile string) (*image.RGBA, error) {
 }
 
 // RenderSetup is called by the material render setup
-func (t *Texture2D) RenderSetup(gs *gls.GLS, idx int) {
+func (t *Texture2D) RenderSetup(gs *gls.GLS, slotIdx, uniIdx int) { // Could have as input - TEXTURE0 (slot) and uni location
 
 	// One time initialization
 	if t.gs == nil {
@@ -307,11 +313,12 @@ func (t *Texture2D) RenderSetup(gs *gls.GLS, idx int) {
 		t.gs = gs
 	}
 
+	// Sets the texture unit for this texture
+	gs.ActiveTexture(uint32(gls.TEXTURE0 + slotIdx))
+	gs.BindTexture(gls.TEXTURE_2D, t.texname)
+
 	// Transfer texture data to OpenGL if necessary
 	if t.updateData {
-		// Sets the texture unit for this texture
-		gs.ActiveTexture(uint32(gls.TEXTURE0 + idx))
-		gs.BindTexture(gls.TEXTURE_2D, t.texname)
 		gs.TexImage2D(
 			gls.TEXTURE_2D, // texture type
 			0,              // level of detail
@@ -331,10 +338,6 @@ func (t *Texture2D) RenderSetup(gs *gls.GLS, idx int) {
 		t.updateData = false
 	}
 
-	// Sets the texture unit for this texture
-	gs.ActiveTexture(uint32(gls.TEXTURE0 + idx))
-	gs.BindTexture(gls.TEXTURE_2D, t.texname)
-
 	// Sets texture parameters if needed
 	if t.updateParams {
 		gs.TexParameteri(gls.TEXTURE_2D, gls.TEXTURE_MAG_FILTER, int32(t.magFilter))
@@ -345,11 +348,16 @@ func (t *Texture2D) RenderSetup(gs *gls.GLS, idx int) {
 	}
 
 	// Transfer texture unit uniform
-	location := t.uniUnit.LocationIdx(gs, int32(idx))
-	gs.Uniform1i(location, int32(idx))
+	var location int32
+	if uniIdx == 0 {
+		location = t.uniUnit.Location(gs)
+	} else {
+		location = t.uniUnit.LocationIdx(gs, int32(uniIdx))
+	}
+	gs.Uniform1i(location, int32(slotIdx))
 
 	// Transfer texture info combined uniform
 	const vec2count = 3
-	location = t.uniInfo.LocationIdx(gs, vec2count*int32(idx))
+	location = t.uniInfo.LocationIdx(gs, vec2count*int32(uniIdx))
 	gs.Uniform2fvUP(location, vec2count, unsafe.Pointer(&t.udata))
 }