Bladeren bron

Added spot lights to mojo3d.

Mark Sibly 7 jaren geleden
bovenliggende
commit
beb6fb7549

+ 38 - 2
modules/mojo3d/assets/shaders/imports/pbr.glsl

@@ -15,13 +15,31 @@ void emitPbrFragment( vec3 color,float metalness,float roughness,vec3 position,v
 	vec3 specular=(color-color0) * metalness + color0;
 	
 #if MX2_DIRECTIONALLIGHT
+
 	vec3 lvec=normalize( -r_LightViewMatrix[2].xyz );
 	float atten=1.0;
-#else
+	
+#elif MX2_POINTLIGHT
+
 	// TODO: https://imdoingitwrong.wordpress.com/2011/01/31/light-attenuation/
 	vec3 lvec=r_LightViewMatrix[3].xyz-position;
 	float atten=max( 1.0-length( lvec )/r_LightRange,0.0 );
 	lvec=normalize( lvec );
+	
+#elif MX2_SPOTLIGHT
+
+	vec3 lvec=r_LightViewMatrix[3].xyz-position;
+	float atten=max( 1.0-length( lvec )/r_LightRange,0.0 );
+	lvec=normalize( lvec );
+	
+	float cosangle=dot( -lvec,r_LightViewMatrix[2].xyz );
+	if( cosangle<0.0 ) return;				//behind spotlight direction
+	
+	float angle=acos( cosangle );
+	if( angle>r_LightOuterAngle ) return;	//outside outer cone
+
+	atten*=1.0-max( (angle-r_LightInnerAngle)/(r_LightOuterAngle-r_LightInnerAngle),0.0 );
+	
 #endif
 
 	vec3 vvec=normalize( -position );
@@ -146,12 +164,30 @@ void emitPbrFragment( vec4 color,vec3 ambient,vec3 emissive,float metalness,floa
 	float fnorm=(spow+2.0)/8.0;							//normalization factor
 	
 #if MX2_DIRECTIONALLIGHT
+
 	vec3 lvec=normalize( -r_LightViewMatrix[2].xyz );
 	float atten=1.0;
-#else
+	
+#elif MX2_POINTLIGHT
+
+	vec3 lvec=r_LightViewMatrix[3].xyz-v_Position;
+	float atten=max( 1.0-length( lvec )/r_LightRange,0.0 );
+	lvec=normalize( lvec );
+	
+#elif MX2_SPOTLIGHT
+
 	vec3 lvec=r_LightViewMatrix[3].xyz-v_Position;
 	float atten=max( 1.0-length( lvec )/r_LightRange,0.0 );
 	lvec=normalize( lvec );
+	
+	float cosangle=dot( -lvec,r_LightViewMatrix[2].xyz );
+	if( cosangle<0.0 ) return;				//behind spotlight direction
+	
+	float angle=acos( cosangle );
+	if( angle>r_LightOuterAngle ) return;	//outside outer cone
+
+	atten*=1.0-max( (angle-r_LightInnerAngle)/(r_LightOuterAngle-r_LightInnerAngle),0.0 );
+	
 #endif
 	vec3 hvec=normalize( lvec+vvec );
 

+ 27 - 9
modules/mojo3d/assets/shaders/imports/std.glsl

@@ -2,7 +2,7 @@
 // **** MX2_RENDERPASS *****
 //
 // PASSTYPE		'mask=3,  0=quad, 1=deferred, 2=forward, 3=shadow
-// LIGHTTYPE	'mask=12, 0=none, 1=directional, 2=point
+// LIGHTTYPE	'mask=12, 0=none, 1=directional, 2=point, 3=spot
 // SHADOWTYPE	'mask=16, 0=no shadows, 1=shadows
 //
 #ifndef MX2_RENDERPASS
@@ -14,6 +14,7 @@
 
 #define MX2_DIRECTIONALLIGHT	(MX2_LIGHTTYPE==1)
 #define MX2_POINTLIGHT			(MX2_LIGHTTYPE==2)
+#define MX2_SPOTLIGHT			(MX2_LIGHTTYPE==3)
 
 #define MX2_QUADPASS			(MX2_PASSTYPE==0)
 #define MX2_DEFERREDPASS		(MX2_PASSTYPE==1)
@@ -21,7 +22,7 @@
 #define MX2_SHADOWPASS			(MX2_PASSTYPE==3)
 
 #define MX2_AMBIENTPASS			(MX2_PASSTYPE==1 || MX2_PASSTYPE==2)
-#define MX2_LIGHTINGPASS		(MX2_LIGHTTYPE!=0 && !MX2_SHADOWPASS)
+#define MX2_LIGHTINGPASS		(MX2_LIGHTTYPE!=0 && MX2_SHADOWPASS==0)
 #define MX2_COLORPASS			(MX2_AMBIENTPASS || MX2_LIGHTINGPASS)
 
 // ***** MX2_ATTRIBMASK *****
@@ -72,14 +73,19 @@ uniform vec2 r_BufferCoordScale;
 uniform mat4 r_LightViewMatrix;
 uniform vec4 r_LightColor;
 uniform float r_LightRange;
-uniform float r_ShadowAlpha;
+uniform float r_LightInnerAngle;
+uniform float r_LightOuterAngle;
+
+//***** SHADOWS *****
+//
 uniform sampler2D r_ShadowCSMTexture;
-uniform vec4 r_ShadowCSMSplits;
 uniform samplerCube r_ShadowCubeTexture;
+uniform vec4 r_ShadowCSMSplits;
 uniform mat4 r_ShadowMatrix0;
 uniform mat4 r_ShadowMatrix1;
 uniform mat4 r_ShadowMatrix2;
 uniform mat4 r_ShadowMatrix3;
+uniform float r_ShadowAlpha;
 
 //***** INSTANCE *****
 //
@@ -260,8 +266,8 @@ vec3 fragmentPosition(){
 float shadowColor( vec3 position ){
 
 #if MX2_DIRECTIONALLIGHT
-
-	if( position.z>=r_ShadowCSMSplits.w ) return 1.0;//(1.0-r_ShadowAlpha)*0.5;
+ 
+	if( position.z>=r_ShadowCSMSplits.w ) return 1.0;
 	
 	vec4 vpos=vec4( position,1.0 );
 	vec2 off;
@@ -288,7 +294,7 @@ float shadowColor( vec3 position ){
 	
 	return 1.0;
 	
-#else
+#elif MX2_POINTLIGHT
 
 	vec4 vpos=vec4( position,1.0 );
 	
@@ -300,6 +306,18 @@ float shadowColor( vec3 position ){
 	
 	return 1.0;
 	
+#elif MX2_SPOTLIGHT
+
+	vec4 vpos=r_ShadowMatrix0 * vec4( position,1.0 );
+	
+	vec3 spos=vpos.xyz/vpos.w * vec3( 0.25,0.25,0.5 ) + vec3( 0.25,0.25,0.5 );
+
+	float d=texture2D( r_ShadowCSMTexture,spos.xy ).r;
+	
+	if( spos.z>d ) return 1.0-r_ShadowAlpha;
+	
+	return 1.0;
+	
 #endif
 }
 
@@ -332,9 +350,9 @@ void emitColorFragment( vec4 color ){
 
 void emitShadowFragment(){
 
-#if MX2_DIRECTIONALLIGHT
+#if MX2_DIRECTIONALLIGHT || MX2_SPOTLIGHT
 	gl_FragColor=vec4( vec3( gl_FragCoord.z ),1.0 );
-#else
+#elif MX2_POINTLIGHT
 	gl_FragColor=FloatToRGBA( min( length( v_Position )/r_LightRange,1.0 ) );
 #endif
 }

+ 1 - 1
modules/mojo3d/assets/shaders/materials/pbr-default.glsl

@@ -1,5 +1,5 @@
 
-//@renderpasses 1,7,11,2,6,10
+//@renderpasses 1,7,11,15,2,6,10,14
 
 //@import "pbr"
 

+ 1 - 1
modules/mojo3d/assets/shaders/misc/lighting-deferred.glsl

@@ -1,5 +1,5 @@
 
-//@renderpasses 4,8,20,24
+//@renderpasses 4,8,12, 20,24,28
 
 //@import "pbr"
 

+ 68 - 23
modules/mojo3d/render/renderer.monkey2

@@ -162,7 +162,6 @@ When a new renderer is created, the config setting `MOJO3D\_RENDERER` can be use
 			color.b=Pow( color.b,2.2 )
 		
 			_gdevice.Clear( color,1.0 )
-
 		Endif
 	End
 	
@@ -172,16 +171,25 @@ When a new renderer is created, the config setting `MOJO3D\_RENDERER` can be use
 		
 		Select light.Type
 		Case LightType.Directional
-			If light.CastsShadow RenderCSMShadows( light ) ; renderPass|=16
+			If light.CastsShadow RenderDirectionalShadows( light ) ; renderPass|=16
 			renderPass|=4
 		Case LightType.Point
-			If light.CastsShadow RenderPSMShadows( light ) ; renderPass|=16
+			If light.CastsShadow RenderPointShadows( light ) ; renderPass|=16
 			renderPass|=8
+		Case LightType.Spot
+			If light.CastsShadow RenderSpotShadows( light ) ; renderPass|=16
+			renderPass|=12
+		Default
+			Return
 		End
 		
+		Local lvmatrix:=_viewMatrix * light.Matrix
+		
+		_runiforms.SetMat4f( "LightViewMatrix",lvmatrix )
 		_runiforms.SetColor( "LightColor",light.Color )
 		_runiforms.SetFloat( "LightRange",light.Range )
-		_runiforms.SetMat4f( "LightViewMatrix",_viewMatrix * light.Matrix )
+		_runiforms.SetFloat( "LightInnerAngle",light.InnerAngle*Pi/180.0 )
+		_runiforms.SetFloat( "LightOuterAngle",light.OuterAngle*Pi/180.0 )
 		
 		_runiforms.SetMat4f( "InverseProjectionMatrix",_invProjMatrix )
 		
@@ -228,11 +236,14 @@ When a new renderer is created, the config setting `MOJO3D\_RENDERER` can be use
 			
 			Select light.Type
 			Case LightType.Directional			
-				If light.CastsShadow RenderCSMShadows( light ) ; renderPass|=16
+				If light.CastsShadow RenderDirectionalShadows( light ) ; renderPass|=16
 				renderPass|=4
 			Case LightType.Point
-				If light.CastsShadow RenderPSMShadows( light ) ; renderPass|=16
+				If light.CastsShadow RenderPointShadows( light ) ; renderPass|=16
 				renderPass|=8
+			Case LightType.Spot
+				If light.CastsShadow RenderSpotShadows( light ) ; renderPass|=16
+				renderPass|=12
 			End
 			
 			_runiforms.SetColor( "LightColor",light.Color )
@@ -311,11 +322,14 @@ When a new renderer is created, the config setting `MOJO3D\_RENDERER` can be use
 			
 			Select light.Type
 			Case LightType.Directional			
-	'			If light.CastsShadow RenderCSMShadows( light ) ; renderPass|=16
+	'			If light.CastsShadow RenderDirectionalShadows( light ) ; renderPass|=16
 				renderPass|=4
 			Case LightType.Point
-	'			If light.CastsShadow RenderPSMShadows( light ) ; renderPass|=16
+	'			If light.CastsShadow RenderPointShadows( light ) ; renderPass|=16
 				renderPass|=8
+			Case LightType.Spot
+	'			If light.CastsShadow RenderSpotShadows( light ) ; renderPass|=16
+				renderPass|=12
 			End
 			
 			_runiforms.SetColor( "LightColor",light.Color )
@@ -345,7 +359,7 @@ When a new renderer is created, the config setting `MOJO3D\_RENDERER` can be use
 		
 	End
 	
-	Method RenderCSMShadows( light:Light )
+	Method RenderDirectionalShadows( light:Light )
 		
 		'Perhaps use a different device for CSM...?
 		'
@@ -366,9 +380,7 @@ When a new renderer is created, the config setting `MOJO3D\_RENDERER` can be use
 		_gdevice.CullMode=CullMode.Front
 		_gdevice.RenderPass=3|4
 
-		Local invLightMatrix:=light.InverseMatrix
-		
-		Local viewLight:=invLightMatrix * -_viewMatrix
+		Local viewLight:=light.InverseMatrix * _invViewMatrix
 		
 		For Local i:=0 Until _csmSplitDepths.Length-1
 			
@@ -412,7 +424,7 @@ When a new renderer is created, the config setting `MOJO3D\_RENDERER` can be use
 			
 			_gdevice.Scissor=_gdevice.Viewport
 				
-			RenderShadowOps( invLightMatrix,lightProj )
+			RenderShadowOps( light.InverseMatrix,lightProj )
 		Next
 		
 		_gdevice.RenderTarget=t_rtarget
@@ -420,7 +432,7 @@ When a new renderer is created, the config setting `MOJO3D\_RENDERER` can be use
 		_gdevice.Scissor=t_scissor
 	End
 	
-	Method RenderPSMShadows( light:Light )
+	Method RenderPointShadows( light:Light )
 	
 		'Perhaps use a different device for CSM...?
 		'
@@ -438,13 +450,12 @@ When a new renderer is created, the config setting `MOJO3D\_RENDERER` can be use
 		_gdevice.CullMode=CullMode.Back'Front
 		_gdevice.RenderPass=3|8
 		
-		Local lnear:=0.1
-		
-		Local lightProj:=Mat4f.Frustum( -lnear,+lnear,-lnear,+lnear,lnear,light.Range )
-		
-		Local invLightMatrix:=light.InverseMatrix
-		
-		Local viewLight:=invLightMatrix * _invViewMatrix
+		Local near:=0.1
+		Local lightProj:=Mat4f.Frustum( -near,+near,-near,+near,near,light.Range )
+		lightProj.k.z=1'(zfar+znear)/(zfar-znear)
+		lightProj.t.z=0'-(zfar*znear*2)/(zfar-znear)
+
+		Local viewLight:=light.InverseMatrix * _invViewMatrix
 		
 		_runiforms.SetFloat( "LightRange",light.Range )
 		_runiforms.SetMat4f( "ShadowMatrix0",viewLight )
@@ -455,7 +466,7 @@ When a new renderer is created, the config setting `MOJO3D\_RENDERER` can be use
 			
 			_gdevice.Clear( Color.White,1.0 )
 			
-			Local viewMatrix:=New AffineMat4f( _psmFaceTransforms[i] ) * invLightMatrix
+			Local viewMatrix:=New AffineMat4f( _psmFaceTransforms[i] ) * light.InverseMatrix
 
 			RenderShadowOps( viewMatrix,lightProj )
 		Next
@@ -465,6 +476,40 @@ When a new renderer is created, the config setting `MOJO3D\_RENDERER` can be use
 		_gdevice.Scissor=t_scissor
 	End
 	
+	Method RenderSpotShadows( light:Light )
+	
+		Local t_rtarget:=_gdevice.RenderTarget
+		Local t_viewport:=_gdevice.Viewport
+		Local t_scissor:=_gdevice.Scissor
+		
+		Local near:=0.1
+		Local lightProj:=Mat4f.Frustum( -near,+near,-near,+near,near,light.Range )
+		
+		Local viewLight:=light.InverseMatrix * _invViewMatrix
+		
+		_runiforms.SetFloat( "LightRange",light.Range )
+		_runiforms.SetMat4f( "ShadowMatrix0",lightProj * viewLight )
+		
+		_gdevice.RenderTarget=_csmTarget
+		_gdevice.Viewport=New Recti( 0,0,_csmTexture.Size/2 )
+		_gdevice.Scissor=_gdevice.Viewport
+		_gdevice.ColorMask=ColorMask.All
+		_gdevice.DepthMask=True
+		
+		_gdevice.Clear( Color.White,1.0 )
+
+		_gdevice.DepthFunc=DepthFunc.LessEqual
+		_gdevice.BlendMode=BlendMode.Opaque
+		_gdevice.CullMode=CullMode.Front'Back
+		_gdevice.RenderPass=3|12
+
+		RenderShadowOps( light.InverseMatrix,lightProj )
+
+		_gdevice.RenderTarget=t_rtarget
+		_gdevice.Viewport=t_viewport
+		_gdevice.Scissor=t_scissor
+	End
+	
 	Method RenderPostEffects()
 		
 		PostEffect.BeginRendering( _gdevice,_runiforms )
@@ -989,7 +1034,7 @@ When a new renderer is created, the config setting `MOJO3D\_RENDERER` can be use
 		
 		For Local r:=Eachin _scene.Renderables
 		
-			_renderQueue.CastsShadow=r.CastsShadow
+			_renderQueue.AddShadowOps=r.CastsShadow
 			
 			r.OnRender( _renderQueue )
 		Next

+ 6 - 14
modules/mojo3d/render/renderqueue.monkey2

@@ -48,13 +48,13 @@ Class RenderQueue
 		_eyeLen=_eyePos.Length
 	End
 	
-	Property CastsShadow:Bool()
+	Property AddShadowOps:Bool()
 		
-		Return _castsShadow
+		Return _addShadowOps
 		
-	Setter( castsShadow:Bool )
+	Setter( addShadowOps:Bool )
 		
-		_castsShadow=castsShadow
+		_addShadowOps=addShadowOps
 	End
 
 	Property OpaqueOps:Stack<RenderOp>()
@@ -100,22 +100,14 @@ Class RenderQueue
 		If op.blendMode=BlendMode.Opaque
 			stack=_opaqueOps
 			op.shader=op.material.GetRenderShader()
+			If _addShadowOps _shadowOps.Add( op )
 		Else
 			stack=_transparentOps
 			op.shader=op.material.GetRenderShader()
 			op.distance=op.instance ? op.instance.Position.Distance( _eyePos ) Else _eyeLen
 		Endif
 		
-		If op.material.SelfIlluminated
-			Assert( False )
-			stack=_selfillumOps
-		Endif
-		
-		If Not op.shader Return
-
 		stack.Add( op )
-		
-		If _castsShadow And op.material.GetRenderShader() _shadowOps.Push( op )
 	End
 	
 	Method AddSpriteOp( op:SpriteOp )
@@ -184,7 +176,7 @@ Class RenderQueue
 	
 	Field _eyePos:Vec3f
 	Field _eyeLen:Float
-	Field _castsShadow:Bool
+	Field _addShadowOps:Bool
 	
 	Field _opaqueOps:=New Stack<RenderOp>
 	Field _selfillumOps:=New Stack<RenderOp>

+ 37 - 6
modules/mojo3d/scene/entities/light.monkey2

@@ -29,8 +29,11 @@ Class Light Extends Entity
 		
 		Name="Light"
 		Type=LightType.Directional
-		CastsShadow=False
+		Color=Color.White
 		Range=10
+		InnerAngle=15
+		OuterAngle=30
+		CastsShadow=False
 		
 		AddInstance()
 		
@@ -83,6 +86,34 @@ Class Light Extends Entity
 		_range=range
 	End
 	
+	#rem monkeydoc The cone inner angle for spot lights, in degrees.
+	
+	Defaults to 0 degrees.
+		
+	#end
+	Property InnerAngle:Float()
+		
+		Return _innerAngle
+	
+	Setter( angle:Float )
+		
+		_innerAngle=angle
+	End
+	
+	#rem monkeydoc The cone outer angle for spot lights, in degrees.
+	
+	Defaults to 45 degrees.
+		
+	#end
+	Property OuterAngle:Float()
+		
+		Return _outerAngle
+	
+	Setter( angle:Float )
+		
+		_outerAngle=angle
+	End
+	
 	Protected
 
 	Method New( light:Light,parent:Entity )
@@ -92,6 +123,9 @@ Class Light Extends Entity
 		Type=light.Type
 		Color=light.Color
 		Range=light.Range
+		InnerAngle=light.InnerAngle
+		OuterAngle=light.OuterAngle
+		CastsShadow=light.CastsShadow
 		
 		AddInstance( light )
 	End
@@ -114,13 +148,10 @@ Class Light Extends Entity
 	Private
 	
 	Field _type:LightType
-	
 	Field _color:Color
-	
 	Field _range:Float
-	
+	Field _innerAngle:Float
+	Field _outerAngle:Float
 	Field _castsShadow:bool
-	
-	Field _dynamic:Bool
 
 End

+ 91 - 0
modules/mojo3d/tests/spotlight.monkey2

@@ -0,0 +1,91 @@
+Namespace myapp3d
+
+#Import "<std>"
+#Import "<mojo>"
+#Import "<mojo3d>"
+
+Using std..
+Using mojo..
+Using mojo3d..
+
+
+Class MyWindow Extends Window
+	
+	Field _scene:Scene
+	
+	Field _camera:Camera
+	
+	Field _light:Light
+	
+	Field _ground:Model
+
+	Field _donut:Model
+	
+	Method New( title:String="Simple mojo3d app",width:Int=640,height:Int=480,flags:WindowFlags=WindowFlags.Resizable )
+		
+		Super.New( title,width,height,flags )
+	End
+	
+	Method OnCreateWindow() Override
+		
+		'create (current) scene
+		_scene=New Scene
+		
+		'create camera
+		_camera=New Camera( Self )
+		_camera.AddComponent<FlyBehaviour>()
+		_camera.Move( 0,2.5,-10 )
+		
+		'create light
+		_light=New Light
+		_light.Type=LightType.Spot
+		_light.Range=25
+		_light.InnerAngle=15
+		_light.OuterAngle=45
+		_light.CastsShadow=True
+		_light.Position=New Vec3f( 0,10,0 )
+		_light.Rotate( 90,0,0 )
+		
+		'create ground
+		Local groundBox:=New Boxf( -100,-1,-100,100,0,100 )
+		Local groundMaterial:=New PbrMaterial( Color.White,0,1 )
+		_ground=Model.CreateBox( groundBox,1,1,1,groundMaterial )
+		_ground.CastsShadow=False
+		
+		'create donut
+		Local donutMaterial:=New PbrMaterial( Color.Red, 0.05, 0.2 )
+		_donut=Model.CreateBox( New Boxf( -1,1 ),1,1,1,donutMaterial )'Model.CreateTorus( 2,.5,48,24,donutMaterial )
+		_donut.Move( 0,2.5,0 )
+	End
+	
+	Method OnRender( canvas:Canvas ) Override
+		
+		RequestRender()
+		
+		If Keyboard.KeyHit( Key.Space )
+			Select _light.Type
+			Case LightType.Directional
+				_light.Type=LightType.Point
+			Case LightType.Point
+				_light.Type=LightType.Spot
+			Case LightType.Spot
+				_light.Type=LightType.Directional
+			End
+		Endif
+		
+		_donut.Rotate( .2,.4,.6 )
+		_scene.Update()
+		_camera.Render( canvas )
+		canvas.DrawText( "FPS="+App.FPS,0,0 )
+	End
+	
+End
+
+Function Main()
+
+	New AppInstance
+	
+	New MyWindow
+	
+	App.Run()
+End