WorldShadowsEx + CreateMaterialEx [PB 6.21]
Posted: Thu Jun 12, 2025 12:03 pm
I'm thinking of completely redoing the "material" part via shaders (abandoning the fixed pipeline)
for this, I've made a CreateMaterialEx function
that handles modern shading (for now, only directional lights are managed), skeletal animation, normal mapping ...
for now, only one light is managed (and not the spotlights)
tell me if there are any effects you can't do with this function (and can do with the basic functions), I'll add them
this is just the 1st version
I'm planning to add a lot more (several lights, spots, several layers... + basic shaders :water, sky...)
for this, I've made a CreateMaterialEx function
that handles modern shading (for now, only directional lights are managed), skeletal animation, normal mapping ...
for now, only one light is managed (and not the spotlights)
tell me if there are any effects you can't do with this function (and can do with the basic functions), I'll add them
this is just the 1st version
I'm planning to add a lot more (several lights, spots, several layers... + basic shaders :water, sky...)
Code: Select all
; WorldShadowsEx(Distance.f,TextureSize=1024,Softness=2,dephbias.f=0.00003)
;===========================================================================
;
; Distance : distance up To which shadows are displayed
;
; TextureSize : size of shadow texture
;
; softness : [1..5] caution, a high value has an impact on performance
;
; dephbias : shadows may show defects if the distance between projection and reception is small. Adjust this value To minimize these defects.
; CreateMaterialEx(material,options,tx1=0,tx2=0,tx3=0,tx4=0)
;============================================================
;
; material : material to be create
;
; options: combination of the following constants
;
; #SO_Txcolor
; use of a color texture
;
; #SO_Txnormal
; use of a normal texture (relief)
; - parameter "bumpy"
;
; #SO_TxEnvCubic
; use of a cubic texture to simulate a reflection
; - parameter"glossy" : reflection intensity [0.0..1.0]
;
; #SO_TxEnvRTT
; use of a texture from an RTT corresponding to the reflection on a plane
; - parameter"glossy" : reflection intensity [0.0..1.0]
;
; #SO_Envfake
; simulates reflection of sky (if normal is up) and ground (if normal is down)
; - parameter "colorground"
; - parameter "colorsky"
; - parametre "groundskytrans" : transition between sky and ground [0.0..1.0] 0:blurred, 1:sharp
; - parameter"glossy": reflection intensity [0.0..1.0]
;
; #SO_Anim
; allows the use of skeletal animation (3 weights maximum)
;
; #SO_VertexColor
; takes vertex color into account
;
; #SO_CustomColor
; allows the use of a custom color For each entity
; use: EntityCustomParameter(#Entity, #SubEntityIndex, 100, red, green, blue, alpha)
; (color components must be defined in the range [0.0..1.0])
;
; #SO_Snow
; simulates snow (if normal is up)
; - parameter "snowcolor"
;
; #SO_Cristal
; gives a crystal appearance
; - parameter "crystal" : number of orientations allowed in each dimension
;
; #SO_AlphaReject
; makes material transparent if alpha component is <128
;
; #SO_NoReceiveShadow
; disables shadow reception
;
; ;#SO_NoCastShadow
; ;disables shadow projection
;
; #SO_Emissive
; deactivates shading to make material emissive
;
;
; tx1, tx2, tx3,tx4
; textures must be added in the following order:
;- color (if #SO_Txcolor)
;- normal (if #SO_Txnormal)
;- cubic Or RTT (if #SO_TxEnvCubic or #SO_TxEnvRTT)
;###################################################################################################################################################
Procedure WorldShadowsEx(Distance.f,TextureSize=1024,Softness=2,dephbias.f=0.00003)
Global osoftness=softness, odephbias.f=dephbias
If softness<1 Or softness>5:MessageRequester("WorldShadowsEx", "Softness must be in range [1..5]"):End:EndIf
DisableDebugger
WorldShadows(4,Distance,0,TextureSize); 4:PB_Shadow_TextureAdditiveIntegrated
SetWorldAttribute(100,32) ;setShadowTexturePixelFormat(PF_FLOAT32_R)
SetWorldAttribute(101,1000) ;setShadowDirLightTextureOffset((unsigned short)value*0.001)
EnableDebugger
EndProcedure
EnumerationBinary shaderoption
#SO_Txcolor
#SO_Txnormal
#SO_TxEnvCubic
#SO_TxEnvRTT
#SO_Envfake
#SO_Shadow
#SO_Anim
#SO_VertexColor
#SO_CustomColor
#SO_Snow
#SO_Cristal
#SO_AlphaReject
#SO_NoReceiveShadow
#SO_NoCastShadow
#SO_Emissive
EndEnumeration
Procedure CreateMaterialEx(material,options,tx1=0,tx2=0,tx3=0,tx4=0)
Static init,mcaster,mcasterA,mcasterR,shadernum=2000:shadernum+1
Protected o=options
Protected.s var,ecl,ver="%#version 130%%",v,f,code,wcoo,wcooa,oprec;
If init=0:init=1
CreateShader(1000,ver+"uniform mat4 P25;//+25%%void main()%{%gl_Position=P25*gl_Vertex;%}",ver+"void main()%{%gl_FragColor=vec4(gl_FragCoord.z,1,1,1);%}")
CreateShader(1001,ver+"attribute vec4 blendIndices;%attribute vec4 blendWeights;%uniform mat3x4 P4[80];//+4%%uniform mat4 P25;//+25%%void main()%{%vec4 opos=vec4(0);%for(int i=0;i<3;i++){%if(blendWeights[i]>0.0){%opos+=vec4(gl_Vertex*P4[int(blendIndices[i])],1)*blendWeights[i];}}%gl_Position=P25*opos;%}",ver+"void main()%{%gl_FragColor=vec4(gl_FragCoord.z,1,1,1);%}")
CreateShader(1002,ver+"uniform mat4 P25;//+25%varying vec2 ouv;%%void main()%{%ouv=(gl_TextureMatrix[0]*gl_MultiTexCoord0).xy;%gl_Position=P25*gl_Vertex;%}",ver+"uniform sampler2D diffuseMap;//0%varying vec2 ouv;%%void main()%{%if (texture(diffuseMap,ouv).a<0.5) discard;%gl_FragColor=vec4(gl_FragCoord.z+"+odephbias+",1,1,1);%}")
mcaster=CreateShaderMaterial(-1,1000)
mcasterA=CreateShaderMaterial(-1,1001)
EndIf
var="vec4 opos,vec3 onormal,vec2 ouv,float ofogf"
v="uniform mat4 world_mx;//+0%uniform mat4 viewproj_mx;//+16%uniform vec4 fog_params;//+32%%@var%%"+
"void main()%{%@wcoo%gl_Position=viewproj_mx*opos;%ouv=(gl_TextureMatrix[0]*gl_MultiTexCoord0).xy;%ofogf=fog_params.z>0?min(abs(gl_Position.z)*fog_params.w,1):0;%@vcolor@ov1%}"
f="uniform float shininess;//+37%uniform vec4 mat_dif;//+34%uniform vec4 mat_spe;//+35%%uniform vec4 ambient_ml;//+69%uniform vec4 light_pos;//+45%uniform vec4 light_dif;//+41%uniform vec4 light_spe;//+42%%uniform vec4 camera_pos;//+79%uniform vec4 fog_colour;//+31%%@var%%"+
"void main()%{%vec3 viewdir=normalize(camera_pos.xyz-opos.xyz);%vec4 dif=vec4(0),spe=vec4(0);%vec4 colt=@txcolor@vcolor@ccolor;@alphar%@txnormal@invn%@txcubic@txrtt@envfake%%@snow"+
"@eclairage%vec4 color=colt@ambdifspe;%@mixenv%gl_FragColor=vec4(mix(color,fog_colour,ofogf).rgb,colt.a);%}"
wcoo="opos=world_mx*gl_Vertex;%%onormal=mat3(world_mx)*gl_Normal;%@otg%"
wcooa="opos=vec4(0);onormal=vec3(0);@otg1%for(int i=0;i<3;i++){%if(blendWeights[i]>0.0){%mat3x4 bonemx=P4[int(blendIndices[i])];%opos+=vec4(gl_Vertex*bonemx,1)*blendWeights[i];%onormal+=(gl_Normal*mat3(bonemx))*blendWeights[i];%@otg2%}%}%opos=world_mx*opos;%onormal=mat3(world_mx)*onormal;%@otg3%"
ecl="%vec3 lightdir=normalize(light_pos.xyz-opos.xyz*light_pos.w);%dif=max(dot(lightdir,normal),0)*mat_dif*light_dif;%spe=pow(max(dot(normalize(lightdir+viewdir),normal),0),shininess)*mat_spe*light_spe*colt.a;%"
;option=0
If osoftness And (o&#SO_NoReceiveShadow=0):o|#SO_shadow:EndIf
Macro shaderMEF(condition,t,rech,remp,def="")
If condition :code=remp:If FindString(remp,"sampler"):tu+1:EndIf:Else:code=def:EndIf
If code<>"-":If rech="":t=code+t:Else:t=ReplaceString(t,"@"+rech,code):EndIf:EndIf
EndMacro
;-------------#SO_txcolor
shaderMEF(o&#SO_txcolor,f,"","uniform sampler2D diffuseMap;//"+tu+"%")
shaderMEF(o&#SO_txcolor,f,"txcolor","texture(diffuseMap,ouv)","vec4(1)")
;-------------#SO_txnormal
shaderMEF(o&#SO_txnormal,wcoo ,"otg","otangent=mat3(world_mx)*tangent;")
shaderMEF(o&#SO_txnormal,wcooa,"otg1","otangent=vec3(0);")
shaderMEF(o&#SO_txnormal,wcooa,"otg2","otangent+=(tangent*mat3(bonemx))*blendWeights[i];")
shaderMEF(o&#SO_txnormal,wcooa,"otg3","otangent=mat3(world_mx)*otangent;")
shaderMEF(o&#SO_txnormal,var,"","vec3 otangent,")
shaderMEF(o&#SO_txnormal,v,"","attribute vec3 tangent;%")
shaderMEF(o&#SO_txnormal,f,"","%uniform sampler2D normalMap;//"+tu+"%uniform float bumpy;//0.3%")
shaderMEF(o&#SO_txnormal,f,"txnormal","vec3 N=normalize(@cristal);%vec3 T=normalize(otangent);%vec3 B=cross(T,N);%vec3 txn=texture(normalMap,ouv).xyz-0.5;%vec3 normal=normalize(txn.z*N+(txn.x*T+txn.y*B)*bumpy);","vec3 normal=normalize(@cristal);%")
;-------------#SO_txEnvCubic
shaderMEF(o&#SO_txEnvCubic,f,"","uniform samplerCube cubemap;//"+tu+"%")
shaderMEF(o&#SO_txEnvCubic,f,"txcubic","vec3 r=reflect(-viewdir,normal);r.z*=-1;%vec4 colr=texture(cubemap,r);%")
;-------------#SO_txEnvRTT
shaderMEF(o&#SO_txEnvRTT,f,"","uniform vec4 viewport;//+113%uniform sampler2D rttmap;//"+tu+"%")
shaderMEF(o&#SO_txEnvRTT,f,"txrtt","vec2 uvrtt=gl_FragCoord.xy*viewport.zw;uvrtt.y=1-uvrtt.y;%@rttn%vec4 colr=texture(rttmap,uvrtt);%")
shaderMEF(o&#SO_txEnvRTT And o&#SO_txnormal,f,"","uniform float fov;//+117%")
shaderMEF(o&#SO_txEnvRTT And o&#SO_txnormal,f,"rttn","vec2 ratio=vec2(viewport.y,viewport.x)*viewport.z*vec2(-1,1)*2/fov;%uvrtt+=txn.xy*ratio*bumpy;")
;-------------#SO_Envfake
shaderMEF(o&#SO_Envfake,f,"","uniform vec4 groundcolor;//0.4 0.3 0 1%uniform vec4 skycolor;//0.5 0.7 1 1%uniform float groundskytrans;//0.8;%")
shaderMEF(o&#SO_Envfake,f,"envfake","vec4 colr=mix(groundcolor,skycolor,smoothstep(groundskytrans-1,1-groundskytrans,normal.y));%")
;-------------
shaderMEF(o&(#SO_txEnvCubic|#SO_txEnvRTT|#SO_Envfake),f,"","uniform float glossy;//0.4%")
shaderMEF(o&(#SO_txEnvCubic|#SO_txEnvRTT|#SO_Envfake),f,"mixenv","color=mix(color,colr,glossy*colt.a);")
tuenv=tu-1
;-------------#SO_alphareject
shaderMEF(o&#SO_alphareject,f,"alphar","if (colt.a<0.5) discard;")
shaderMEF(o&#SO_alphareject,f,"invn","if (!gl_FrontFacing) normal*=-1;")
;-------------vertexcolor
shaderMEF(o&#SO_vertexcolor,var,"","vec4 ovcolor,")
shaderMEF(o&#SO_vertexcolor,v,"vcolor","ovcolor=gl_Color;%")
shaderMEF(o&#SO_vertexcolor,f,"vcolor","*ovcolor")
;-------------customcolor
shaderMEF(o&#SO_customcolor,f,"","%uniform vec4 ccolor;//+90 100%")
shaderMEF(o&#SO_customcolor,f,"ccolor","*ccolor")
;-------------snow
shaderMEF(o&#SO_snow,f,"","%uniform vec4 snowcolor;//0.8 0.9 1 0%")
shaderMEF(o&#SO_snow,f,"snow","if (normal.y>0.6) colt=snowcolor;%")
;-------------#SO_cristal
shaderMEF(o&#SO_cristal,f,"","%uniform float cristal;//3%")
shaderMEF(o&#SO_cristal,f,"cristal","floor(onormal*cristal)/cristal","onormal")
;-------------#SO_Emissive
shaderMEF(o&#SO_Emissive,f,"eclairage","","-")
shaderMEF(o&#SO_Emissive,f,"ambdifspe","","*(ambient_ml+dif)+spe")
;-------------animation
shaderMEF(o&#SO_anim,v,"","%attribute vec4 blendIndices;%attribute vec4 blendWeights;%%uniform mat3x4 P4[80];//+4%")
shaderMEF(o&#SO_anim,v,"wcoo",wcooa,wcoo)
;-------------ombre
oprec="ombre/=n2"
;oprec="ombre=smoothstep(0,n2,ombre)"
shaderMEF(o&#SO_shadow,var,"","vec4 oshuv,")
shaderMEF(o&#SO_shadow,v,"","%uniform mat4 texViewProj;//+82 0%")
shaderMEF(o&#SO_shadow,v,"ov1","%oshuv=texViewProj*opos;%")
shaderMEF(o&#SO_shadow,f,"","%uniform sampler2DShadow shadowMap;//"+tu+"%uniform vec4 inverse_texture_size;//+129 "+tu+"%")
shaderMEF(o&#SO_shadow,f,"eclairage","%const int n="+osoftness+",n2=n*n;%const float amp=(n-1)*0.5;%vec4 shuv=oshuv;%vec2 shdtex=inverse_texture_size.xy*shuv.w;%shuv.z=shuv.z*0.5+0.5;%float ombre=0;%for(float y=-amp;y<=amp;y+=1.0)%for(float x=-amp;x<=amp;x+=1.0)%ombre+=textureProj(shadowMap,shuv+vec4(vec2(x,y)*shdtex,0,0));%"+oprec+";%if(ombre>0){%"+ecl+"dif*=ombre;%spe*=ombre;%}",ecl)
var="varying "+ReplaceString(var,",",";%varying ")+";"
v=ReplaceString(v,"@var",var)
f=ReplaceString(f,"@var",var)
CreateShader(shadernum,ver+v,ver+f)
;Debug ReplaceString(v,"%",#LF$):Debug "------------------------":Debug ReplaceString(f,"%",#LF$):Debug "===========================";:End
num=CreateShaderMaterial(material,shadernum):If material=-1:material=num:EndIf
MaterialShaderTexture(material,tx1,tx2,tx3,tx4)
If o&#SO_txEnvCubic Or o&#SO_txEnvRTT:SetMaterialAttribute(material,#PB_Material_TAM,#PB_Material_ClampTAM,tuenv):EndIf
DisableDebugger
If (o&#SO_anim)=0 :SetMaterialAttribute(material,100,mcaster):EndIf
If (o&#SO_anim)<>0:SetMaterialAttribute(material,100,mcastera):EndIf
If (o&#SO_alphareject)
mcasterR=CreateShaderMaterial(-1,1002):MaterialCullingMode(mcasterR,#PB_Material_NoCulling):MaterialShaderTexture(mcasterR,tx1,0,0,0)
SetMaterialAttribute(material,100,mcasterR)
EndIf
EnableDebugger
MaterialShininess(material,64,0);bug
ProcedureReturn material
EndProcedure
;###################################################################################################################################################
;###################################################################################################################################################
;###################################################################################################################################################
Procedure.f POM(v.f)
ProcedureReturn (Random(v*1000)-v*500)/500
EndProcedure
InitEngine3D(#PB_Engine3D_DebugLog)
InitSprite()
InitKeyboard()
InitMouse()
ExamineDesktops():dx=DesktopWidth(0):dy=DesktopHeight(0)
OpenWindow(0, 0,0, DesktopUnscaledX(dx),DesktopUnscaledY(dy), "Terrain - [Esc] quit",#PB_Window_ScreenCentered)
OpenWindowedScreen(WindowID(0), 0, 0, dx, dy, 0, 0, 0)
Add3DArchive(#PB_Compiler_Home + "examples/3d/Data/Main", #PB_3DArchive_FileSystem)
Add3DArchive(#PB_Compiler_Home + "examples/3d/Data/Textures", #PB_3DArchive_FileSystem)
Add3DArchive(#PB_Compiler_Home + "examples/3d/Data/Models", #PB_3DArchive_FileSystem)
Add3DArchive(#PB_Compiler_Home + "examples/3d/Data/Packs/desert.zip", #PB_3DArchive_Zip)
Parse3DScripts()
WorldShadowsEx(200,1024,3,0.00002)
;- Light
;CreateLight(0 ,$ffffff, 0, 50, 0)
;CreateLight(0 ,$ffffff, 0, 50, 0,#SO_PB_Light_Spot):LightDirection(0 ,0,-1,0):SpotLightRange(0,20,80)
CreateLight(0 ,$ffffff, 0, 20, 0,#PB_Light_Directional):LightDirection(0 ,-1,-1,-0.5)
AmbientColor($111111*7)
;- Camera
CreateCamera(0, 0, 0, 100, 100)
MoveCamera(0, 100, 20, 0, #PB_Absolute)
CameraLookAt(0,0,0,0)
CameraBackColor(0, $444444)
SkyBox("desert07.jpg")
tx=LoadTexture(-1, "Wood.jpg")
txn=LoadTexture(-1,"waternormal.png")
txc=LoadTexture(-1,"desert07.jpg")
txr=LoadTexture(-1,"r2skin.jpg")
txa=CreateTexture(-1,512,512):StartDrawing(TextureOutput(txa)):DrawingMode(#PB_2DDrawing_AllChannels):Box(0,0,512,512,$ffcccccc):Box(6,6,500,500,0):For i=0 To 500:c=RGBA(Random(250,100),100,0,255):p=Random(500)+6:Box(p,6,1,500,c):Box(6,p,500,1,c):Next:Circle(256,256,200,0):StopDrawing()
txm=CreateTexture(-1,512,512):StartDrawing(TextureOutput(txm)):DrawingMode(#PB_2DDrawing_AllChannels):For i=0 To 127:Box(i,i,512-2*i,512-2*i,RGBA(255,255,255,i*2)):Next:StopDrawing()
;------------- mirror
CreateCamera(1,0,0,100,100)
txrtt=CreateRenderTexture(-1,CameraID(1),ScreenWidth()/2,ScreenHeight()/2)
matm=CreateMaterialEx(-1,#SO_Txnormal|#SO_TxEnvRTT, TextureID(txn),TextureID(txrtt)):MaterialShaderParameter(matm,1,"bumpy",1, 0.01,0,0,0)
MaterialShaderParameter(matm,1,"glossy",1, 1,0,0,0)
MaterialShininess(matm,16,$111111*8)
CreatePlane(0,64,36,1,1,1,1):BuildMeshTangents(0)
em=CreateEntity(-1,MeshID(0),MaterialID(matm),250,28,0):RotateEntity(em,90,-90,0)
;------------- ground
matp=CreateMaterialEx(-1,#SO_Txcolor|#SO_TxEnvCubic, TextureID(tx),TextureID(txc))
MaterialFilteringMode(matp,#PB_Material_Anisotropic,4)
MaterialShininess(matp,128*4,$111111*15)
CreatePlane(0,800,800,1,1,20,20):BuildMeshTangents(0)
CreateEntity(-1,MeshID(0),MaterialID(matp),0,0,0)
;------------- objects
mato=CreateMaterialEx(-1,#SO_Txcolor|#SO_Txnormal|#SO_TxEnvCubic|#SO_CustomColor, TextureID(tx),TextureID(txn),TextureID(txc))
MaterialFilteringMode(mato,#PB_Material_Anisotropic,4)
MaterialShaderParameter(mato,1,"bumpy",1, -0.2,0,0,0)
CreateSphere(1,6,32,32)
CreateTorus(2,4*2,0.6*4,32,32)
CreateCube(3,12)
CreateTube(4,6,4,10,32,1)
RandomSeed(0)
For i=1 To 200
m=Random(3)+1
e=CreateEntity(-1,MeshID(m),MaterialID(mato),pom(140),40+pom(40),pom(140))
RotateEntity(e,Random(360),Random(360),Random(360),#PB_Absolute)
c=Random($ffffff)
EntityCustomParameter(e,0,100,Red(c)/255,Green(c)/255,Blue(c)/255,1)
Next
;------------- robot
LoadMesh(5, "robot.mesh"):BuildMeshTangents(5)
matr=CreateMaterialEx(-1,#SO_Txcolor|#SO_txEnvCubic|#SO_Anim,TextureID(txr),TextureID(txc))
MaterialShaderParameter(matr,1,"glossy",1, 0.3,0,0,0)
MaterialShininess(matr,128,$111111*8)
For i=1 To 10
e=CreateEntity(-1,MeshID(5),MaterialID(matr),0,0,(i-5)*50):ScaleEntity(e,0.5,0.5,0.5):RotateEntity(e,0,pom(360),0)
StartEntityAnimation(e, "Walk"):AddEntityAnimationTime(e,"Walk",Random(5000))
Next
;------------- alphareject
matp=CreateMaterialEx(-1,#SO_Txcolor|#SO_Txnormal|#SO_AlphaReject, TextureID(txa),TextureID(txn))
MaterialCullingMode(matp,#PB_Material_NoCulling)
MaterialShaderParameter(matp,1,"bumpy",1, 0.4,0,0,0)
MaterialShininess(matp,64,$111111*4)
CreateCube(0,100):BuildMeshTangents(0)
CreateEntity(-1,MeshID(0),MaterialID(matp),0,120-80,320)
Procedure CameraUserControl(camera,speed.f=0.1,smooth.f=0.1,yfixed.f=1e10)
Static.f MouseX,Mousey,depx,depz,sdepx,sdepz, fdf.b
depx=-speed*(KeyboardPushed(#PB_Key_Left)-KeyboardPushed(#PB_Key_Right))
depz=-speed*(KeyboardPushed(#PB_Key_Down)-KeyboardPushed(#PB_Key_Up)-MouseWheel()*20)
If KeyboardReleased(#PB_Key_F12):fdf=1-fdf:If fdf:CameraRenderMode(0,#PB_Camera_Wireframe):Else:CameraRenderMode(0,#PB_Camera_Textured):EndIf:EndIf
MouseX = -MouseDeltaX() * 0.05
MouseY = -MouseDeltaY() * 0.05
RotateCamera(camera, MouseY, MouseX, 0, #PB_Relative)
sdepx+(depx-sdepx)*smooth
sdepz+(depz-sdepz)*smooth
MoveCamera (camera, sdepX, 0, -sdepz)
If yfixed<>1e10:MoveCamera(camera,CameraX(camera),yfixed,CameraZ(camera),#PB_Absolute):EndIf
EndProcedure
Define.f MouseX,Mousey,depx,depz,dist,val,x,y,z,a,al=3.64,t
Repeat
While WindowEvent():Wend
ExamineKeyboard()
ExamineMouse()
CameraUserControl(0,0.5)
al+(KeyboardPushed(#PB_Key_Add)-KeyboardPushed(#PB_Key_Subtract))*0.01:LightDirection(0,Cos(al),-0.6,Sin(al))
CameraReflection(1,0,EntityID(em))
RenderWorld()
FlipBuffers()
SetWindowTitle(0,"FPS: "+ Engine3DStatus(#PB_Engine3D_AverageFPS))
Until KeyboardReleased(#PB_Key_Escape) Or MouseButton(3)