I noticed that on the calculation for the point light, the color of the light was red (0,0,1)
and the base color is blue (1, 0, 0)
and thus when we were having the calculation of color we have color = lightColor*baseColor*diffuseIntensity = (0,0,0)
. So we change the base color to be (1,1,1)
in order to see it. I think this is wrong, this is not how light works. If we have red and blue we can combine them to be (1,0 ,1)
. I think therefore that formulas in the book must be wrong. I have change it to float3 color = (light.color+baseColor)*diffuseIntensity;
for Sunlight and for point light.
I am sure this is a vast subject and I do not know enough about it but at least I can see that this would work better. Also I notice an error, for the calculation of reflection.
The reflection should have as first argument the incidence of light not the position of light, that is incidence of light is the negative of the position of light. On your calculation for camera position you have in.worldPosition - fragmentsUniform.cameraPosition
but it should be fragmentsUniform.cameraPosition - in.worldPosition
, since you had the negative of the incidence everything was coming ok. But this way it should be clearer.
Also for the angle of the specular light, rising to the 32 power only brings numbers less than 1 very close to 0. However this is still done in a continuous way. If we want to have a cone that cuts
the light we need could check when the dot product is smaller than some threshold. One could take the angle that ones to use and take cosine of it to be the threshold. I just directly used 0.25, this will create a discontinuity, i.e. a proper cone and thus an effect of shining. Below is my code. Hope someone finds it useful.
#include <metal_stdlib>
using namespace metal;
#import "Common.h"
struct VertexIn {
float4 position [[ attribute(0) ]];
float3 normal [[ attribute(1)]];
};
struct VertexOut {
float4 position [[ position ]];
float3 worldPosition;
float3 worldNormal;
};
vertex VertexOut vertex_main(const VertexIn vertexIn [[ stage_in ]],
constant Uniforms &uniforms [[ buffer(1) ]]) {
VertexOut out;
out.position = uniforms.projectionMatrix*uniforms.viewMatrix*uniforms.modelMatrix*vertexIn.position;
out.worldPosition = (uniforms.modelMatrix*vertexIn.position).xyz;
out.worldNormal = uniforms.normalMatrix*vertexIn.normal;
return out;
}
fragment float4 fragment_main( VertexOut in [[ stage_in ]],
constant Light *lights [[ buffer(2) ]],
constant FragmentUniforms &fragmentUniforms [[ buffer(3)]]) {
float3 baseColor = float3(0, 0, 1);
float3 ambienColor = 0;
float3 diffuseColor = 0;
float3 specularColor = 0;
float materialShininess = 32;
float3 materialSpecularColor = float3(0.1, 0.1, 0.5);
float3 normalDirection = normalize(in.worldNormal);
for (uint i=0; i< fragmentUniforms.lightCount; i++) {
Light light = lights[i];
if (light.type == Sunlight) {
float3 lightDirection = normalize(light.position);
float diffuseIntensity = saturate(dot(lightDirection, normalDirection));
if (diffuseIntensity > 0) {
diffuseColor += light.intensity*light.color* baseColor*diffuseIntensity;
float3 reflection = reflect(-lightDirection, normalDirection);
float3 cameraPosition = normalize( fragmentUniforms.cameraPosition - in.worldPosition);
float specularIntensity = pow(saturate(dot(reflection, cameraPosition)), materialShininess);
if (specularIntensity > 0.25) {
specularColor += (light.specularColor + baseColor)*specularIntensity;
}
}
}
if (light.type == Ambientlight) {
float3 cameraPosition = normalize( fragmentUniforms.cameraPosition - in.worldPosition);
float ambientSpecularIntensity = dot(in.worldNormal, cameraPosition);
ambienColor += (baseColor + light.color)*light.intensity*ambientSpecularIntensity;
}
if (light.type == Pointlight) {
float d = distance(light.position, in.worldPosition);
float3 lightDirection = normalize(light.position - in.worldPosition);
float attenuation = 1.0/(light.attenuation.x + light.attenuation.y*d + light.attenuation.z*d*d);
float diffuseIntensity = saturate(dot(lightDirection, normalDirection));
float3 color = (light.color+baseColor)*diffuseIntensity;
color *= attenuation;
diffuseColor += color;
}
}
float3 color = specularColor + diffuseColor + ambienColor;
return float4(color, 1);
}