/usr/src/castle-game-engine-5.0.0/x3d/x3dnodes_lightcontribution.inc is in castle-game-engine-src 5.0.0-3.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
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 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 | { Implement
TLightInstance.Contribution and
TLightInstance.ContributionCameraIndependent.
Note: In some cases we have to do something different than VRML 2.0/X3D spec:
@unorderedList(
@item(
For VRML 1.0 SpotLight, we have to calculate spot light differently
(because VRML 1.0 SpotLight gives me dropOffRate instead of
beamWidth), so we use spot factor equation following OpenGL equations.)
@item(
For VRML 1.0, we have to calculate ambientFactor in a little different way:
see [http://castle-engine.sourceforge.net/x3d_extensions.php#ext_light_attenuation],
when light's ambientIntensity is < 0 then we just return 0 and
otherwise we use material's ambientColor.)
@item(
VRML 97 lighting equations suggest one-sided lighting, only where
the normal points out. In my opinion, one-sided lighting is not useful,
and also our OpenGL rendering uses two-sides lighting.
(One reason for OpenGL rendering is to integrate nicely with flat mirrors,
where you have to flip normals. So OpenGL renderer always gives
vectors from CCW, and so uses two-side to not favor any side of the face.))
)
}
var Spot: Single;
Attenuat: Single;
Factors: TVector3Single;
{ kierunek od Point do zrodla swiatla, normalized }
LightDirNorm: TVector3Single;
{ wektor normalny do powierzchni (skierowany w strone Light) }
Normal: TVector3Single;
PosLightNode: TAbstractPositionalLightNode;
{$ifndef CAMERA_INDEP}
M1: TMaterialNode_1;
M2: TMaterialNode;
{ srednia z wektora do cameraa i LightDirNorm, potrzebna do liczenia
odbicia Specular }
CameraAndLightHalfway: TVector3Single;
MaterialAmbientIntensity: Single;
MaterialAmbientColor: TVector3Single;
MaterialDiffuseColor: TVector3Single;
MaterialSpecularColor: TVector3Single;
MaterialShininessExp: Single;
{$endif}
function CalculateSpot_1(SpotLight: TSpotLightNode_1): Single;
var
CosSpotAngle, SpotAngle: Single;
begin
{ Uzywamy dropOffRate VRMLa 1.0 zeby sprawic by spot byl
bardziej "skupiony" zgodnie z rownaniami swiatla OpenGLa.
W VRMLu 97 skupienie spota jest okreslane inaczej, przez beamWidth. }
CosSpotAngle := (-LightDirNorm) ** Direction;
{ TODO: optimize: no need to calc ArcCos here if we would precalc
CosCutOffAngle for every light. }
SpotAngle := ArcCos(CosSpotAngle);
if SpotAngle <= SpotLight.FdCutOffAngle.Value then
Result := Power(CosSpotAngle, SpotLight.SpotExponent) else
Result := 0;
end;
function CalculateSpot(SpotLight: TSpotLightNode): Single;
var
CosSpotAngle, SpotAngle, SpotCO, SpotBW: Single;
begin
CosSpotAngle := (-LightDirNorm) ** Direction;
SpotAngle := ArcCos(CosSpotAngle);
SpotCO := SpotLight.FdCutOffAngle.Value;
SpotBW := SpotLight.FdBeamWidth.Value;
if SpotAngle < SpotCO then
begin
if SpotAngle > SpotBW then
Result := (SpotAngle - SpotCO) / (SpotBW - SpotCO) else
Result := 1;
end else
Result := 0;
end;
begin
{ wyciagnij ze struktur kilka zmiennych ktorych bedziemy intensywnie uzywac }
{$ifndef CAMERA_INDEP}
M1 := nil;
M2 := nil;
if State.ShapeNode <> nil then
begin
M2 := State.ShapeNode.Material;
if M2 <> nil then
begin
MaterialAmbientIntensity := M2.FdAmbientIntensity.Value;
MaterialDiffuseColor := M2.FdDiffuseColor.Value;
MaterialSpecularColor := M2.FdSpecularColor.Value;
MaterialShininessExp := M2.ShininessExp;
end else
begin
{ Default VRML 2.0 lighting properties. }
MaterialAmbientIntensity := DefaultMaterialAmbientIntensity;
MaterialDiffuseColor := DefaultMaterialDiffuseColor;
MaterialSpecularColor := DefaultMaterialSpecularColor;
MaterialShininessExp := DefaultMaterialShininess * 128.0;
end;
end else
begin
M1 := State.LastNodes.Material;
MaterialAmbientColor := M1.AmbientColor3Single(0);
MaterialDiffuseColor := M1.DiffuseColor3Single(0);
MaterialSpecularColor := M1.SpecularColor3Single(0);
MaterialShininessExp := M1.ShininessExp(0);
end;
{$endif}
if (not Node.FdOn.Value) then
begin
Result := ZeroVector3Single;
Exit;
end;
{ TODO: arg dla attenuation powinien byc w ukladzie transformacji swiatla.
For now lights with non-trivial attenuation put under some transformation
that does distance scaling will not render as required by VRML 2.0 spec.
Czyli musze implementowac InvertTransformation ? }
if Node is TAbstractPositionalLightNode then
begin
PosLightNode := TAbstractPositionalLightNode(Node);
if PosLightNode.DistanceNeededForAttenuation then
Attenuat := PosLightNode.Attenuation( PointsDistance(Point, Location) ) else
Attenuat := PosLightNode.Attenuation( Single(0.0) );
end else
Attenuat := 1;
{ LightDirNorm is called "L" in the equation in VRML spec }
if Node is TAbstractPositionalLightNode then
LightDirNorm := Normalized(Location - Point) else
LightDirNorm := -Direction;
{ wektory Normal i LightDirNorm musza wychodzic z tej samej strony
powierzchni TriNormPlaneSingle (musimy przeciez zrobic cos co w OpenGLu
jest okreslane jako two-sided lighting, czyli wziac albo Normal albo
-Normal, w zaleznosci od pozycji swiatla.). }
{ Normal is called "N" in the equation in VRML spec }
Normal := PlaneDirInDirection(PointPlaneNormal, LightDirNorm);
{$ifndef CAMERA_INDEP}
{ niestety musimy znormalizowac (CamPosition - Point)
(zeby mial taki sam wklad w sume co LightDirNorm) i wynik
(ktory musi byc znormalizowany zeby nadawal sie do wyliczania
cosinusa jako prostego dot product; jak zwykle, nalezy pamietac
ze suma dwoch wektorow dlugosci 1 niekoniecznie (rzadko) jest wektorem
dlugosci 2 (wiec skalowanie przez 1/2 nie rozwiazaloby tu problemu)) }
CameraAndLightHalfway := Normalized(
Normalized(CamPosition - Point) + LightDirNorm);
{$endif}
{ oblicz Spot - dla SpotLight okresla on jak bardzo punkt jest w spocie,
dla innych swiatel jest zawsze = 1. }
if Node is TSpotLightNode_1 then
Spot := CalculateSpot_1(TSpotLightNode_1(Node)) else
if Node is TSpotLightNode then
Spot := CalculateSpot(TSpotLightNode(Node)) else
Spot := 1;
Result := Node.FdColor.Value * (Attenuat * Spot);
{ Now we calculate "ambient_i" part of equation.
Note special things for VRML 1.0:
- when light.ambientIntensity < 0 then we assume we have to
be compatible to our initial VRML 1.0 behavior and
ambient factor is just zero.
- when light.ambientIntensity >= 0 but this is still VRML 1.0 material
then instead of material.ambientIntensity we take
material.ambientColor. Default material.ambientIntensity
in VRML 2.0 is 0.2 and default material.ambientColor in VRML 1.0
is (0.2, 0.2, 0.2), so results should match for VRML 1.0 and 2.0
for default situations. }
{$ifdef CAMERA_INDEP}
Factors := ZeroVector3Single;
{$else}
if Node.FdAmbientIntensity.Value < 0 then
Factors := ZeroVector3Single else
if M1 <> nil then
Factors := (MaterialDiffuseColor * MaterialAmbientColor) *
Node.FdAmbientIntensity.Value else
Factors := MaterialDiffuseColor *
(Node.FdAmbientIntensity.Value * MaterialAmbientIntensity);
{$endif}
{ Factors += diffuse factor }
Factors += MaterialDiffuseColor *
(Node.FdIntensity.Value * (Normal ** LightDirNorm));
{$ifndef CAMERA_INDEP}
{ Factors += specular factor }
Factors += MaterialSpecularColor *
(Node.FdIntensity.Value *
Power( Max(Normal ** CameraAndLightHalfway, Single(0.0)),
MaterialShininessExp));
{$endif}
Result *= Factors;
end;
|