diff --git a/src/g_shared/a_dynlightdata.cpp b/src/g_shared/a_dynlightdata.cpp index 817e7a80b..320f51019 100644 --- a/src/g_shared/a_dynlightdata.cpp +++ b/src/g_shared/a_dynlightdata.cpp @@ -56,6 +56,7 @@ int ScriptDepth; void gl_InitGlow(FScanner &sc); void gl_ParseBrightmap(FScanner &sc, int); +void gl_ParseMaterial(FScanner &sc, int); void gl_DestroyUserShaders(); void gl_ParseHardwareShader(FScanner &sc, int deflump); void gl_ParseDetailTexture(FScanner &sc); @@ -963,7 +964,8 @@ static const char *CoreKeywords[]= "hardwareshader", "detail", "#include", - NULL + "material", + nullptr }; @@ -985,6 +987,7 @@ enum TAG_HARDWARESHADER, TAG_DETAIL, TAG_INCLUDE, + TAG_MATERIAL }; @@ -1342,6 +1345,9 @@ static void DoParseDefs(FScanner &sc, int workingLump) case TAG_BRIGHTMAP: gl_ParseBrightmap(sc, workingLump); break; + case TAG_MATERIAL: + gl_ParseMaterial(sc, workingLump); + break; case TAG_HARDWARESHADER: gl_ParseHardwareShader(sc, workingLump); break; diff --git a/src/gl/data/gl_data.cpp b/src/gl/data/gl_data.cpp index 42fc6e96d..ca3e25ab9 100644 --- a/src/gl/data/gl_data.cpp +++ b/src/gl/data/gl_data.cpp @@ -68,7 +68,7 @@ CUSTOM_CVAR(Bool, gl_notexturefill, false, 0) void gl_CreateSections(); -void AddAutoBrightmaps(); +void AddAutoMaterials(); //----------------------------------------------------------------------------- // @@ -364,7 +364,7 @@ void gl_RecalcVertexHeights(vertex_t * v) void gl_InitData() { AdjustSpriteOffsets(); - AddAutoBrightmaps(); + AddAutoMaterials(); } //========================================================================== diff --git a/src/gl/renderer/gl_renderstate.cpp b/src/gl/renderer/gl_renderstate.cpp index 81672b63c..b27a5de59 100644 --- a/src/gl/renderer/gl_renderstate.cpp +++ b/src/gl/renderer/gl_renderstate.cpp @@ -87,6 +87,8 @@ void FRenderState::Reset() mSpecialEffect = EFF_NONE; mClipHeight = 0.f; mClipHeightDirection = 0.f; + mGlossiness = 0.0f; + mSpecularLevel = 0.0f; mShaderTimer = 0.0f; ClearClipSplit(); @@ -137,7 +139,7 @@ bool FRenderState::ApplyShader() } else { - activeShader = GLRenderer->mShaderManager->Get(mTextureEnabled ? mEffectState : 4, mAlphaThreshold >= 0.f, mPassType); + activeShader = GLRenderer->mShaderManager->Get(mTextureEnabled ? mEffectState : SHADER_NoTexture, mAlphaThreshold >= 0.f, mPassType); activeShader->Bind(); } @@ -178,6 +180,7 @@ bool FRenderState::ApplyShader() activeShader->muLightIndex.Set(mLightIndex); // will always be -1 for now activeShader->muClipSplit.Set(mClipSplit); activeShader->muViewHeight.Set(viewheight); + activeShader->muSpecularMaterial.Set(mGlossiness, mSpecularLevel); if (mGlowEnabled) { diff --git a/src/gl/renderer/gl_renderstate.h b/src/gl/renderer/gl_renderstate.h index 6de2b1455..09888db5c 100644 --- a/src/gl/renderer/gl_renderstate.h +++ b/src/gl/renderer/gl_renderstate.h @@ -94,6 +94,7 @@ class FRenderState bool mLastDepthClamp; float mInterpolationFactor; float mClipHeight, mClipHeightDirection; + float mGlossiness, mSpecularLevel; float mShaderTimer; FVertexBuffer *mVertexBuffer, *mCurrentVertexBuffer; @@ -161,6 +162,7 @@ public: } mEffectState = overrideshader >= 0? overrideshader : mat->mShaderIndex; mShaderTimer = mat->tex->gl_info.shaderspeed; + SetSpecular(mat->tex->gl_info.Glossiness, mat->tex->gl_info.SpecularLevel); mat->Bind(clampmode, translation); } @@ -395,6 +397,12 @@ public: mObjectColor2 = pe; } + void SetSpecular(float glossiness, float specularLevel) + { + mGlossiness = glossiness; + mSpecularLevel = specularLevel; + } + void SetFog(PalEntry c, float d) { const float LOG2E = 1.442692f; // = 1/log(2) diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index 178b39ff7..060eaf6a3 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -331,6 +331,8 @@ void GLSceneDrawer::RenderScene(int recursion) gl_drawinfo->drawlists[GLDL_MASKEDWALLSOFS].DrawWalls(GLPASS_LIGHTSONLY); gl_drawinfo->drawlists[GLDL_TRANSLUCENTBORDER].Draw(GLPASS_LIGHTSONLY); gl_drawinfo->drawlists[GLDL_TRANSLUCENT].Draw(GLPASS_LIGHTSONLY, true); + gl_drawinfo->drawlists[GLDL_MODELS].Draw(GLPASS_LIGHTSONLY); + SetupWeaponLight(); GLRenderer->mLights->Finish(); } diff --git a/src/gl/scene/gl_scenedrawer.h b/src/gl/scene/gl_scenedrawer.h index 9ade64b73..9ca7132f1 100644 --- a/src/gl/scene/gl_scenedrawer.h +++ b/src/gl/scene/gl_scenedrawer.h @@ -14,6 +14,10 @@ class GLSceneDrawer subsector_t *currentsubsector; // used by the line processing code. sector_t *currentsector; + TMap weapondynlightindex; + + void SetupWeaponLight(); + void RenderMultipassStuff(); void UnclipSubsector(subsector_t *sub); diff --git a/src/gl/scene/gl_sprite.cpp b/src/gl/scene/gl_sprite.cpp index 74e5b8ed9..af7eec06c 100644 --- a/src/gl/scene/gl_sprite.cpp +++ b/src/gl/scene/gl_sprite.cpp @@ -82,6 +82,7 @@ EXTERN_CVAR (Bool, r_debug_disable_vis_filter) extern TArray sprites; extern TArray SpriteFrames; extern uint32_t r_renderercaps; +extern int modellightindex; enum HWRenderStyle { @@ -263,7 +264,25 @@ void GLSprite::CalculateVertices(FVector3 *v) void GLSprite::Draw(int pass) { - if (pass == GLPASS_DECALS || pass == GLPASS_LIGHTSONLY) return; + if (pass == GLPASS_DECALS) return; + + if (pass == GLPASS_LIGHTSONLY) + { + if (modelframe) + { + if (RenderStyle.BlendOp != STYLEOP_Shadow) + { + if (gl_lights && GLRenderer->mLightCount && mDrawer->FixedColormap == CM_DEFAULT && !fullbright) + { + if (!particle) + { + dynlightindex = gl_SetDynModelLight(gl_light_sprites ? actor : nullptr, -1); + } + } + } + } + return; + } bool additivefog = false; bool foglayer = false; @@ -336,7 +355,7 @@ void GLSprite::Draw(int pass) if (gl_lights && GLRenderer->mLightCount && mDrawer->FixedColormap == CM_DEFAULT && !fullbright) { if (modelframe && !particle) - gl_SetDynModelLight(gl_light_sprites ? actor : NULL); + gl_SetDynModelLight(gl_light_sprites ? actor : NULL, dynlightindex); else gl_SetDynSpriteLight(gl_light_sprites ? actor : NULL, gl_light_particles ? particle : NULL); } @@ -990,7 +1009,7 @@ void GLSprite::Process(AActor* thing, sector_t * sector, int thruportal) if (gl_fuzztype != 0 && !gl.legacyMode && !(RenderStyle.Flags & STYLEF_InvertSource)) { RenderStyle = LegacyRenderStyles[STYLE_Translucent]; - OverrideShader = gl_fuzztype + 4; + OverrideShader = SHADER_NoTexture + gl_fuzztype; trans = 0.99f; // trans may not be 1 here hw_styleflags = STYLEHW_NoAlphaTest; } diff --git a/src/gl/scene/gl_spritelight.cpp b/src/gl/scene/gl_spritelight.cpp index 1415ce16c..72f69cb7d 100644 --- a/src/gl/scene/gl_spritelight.cpp +++ b/src/gl/scene/gl_spritelight.cpp @@ -198,13 +198,21 @@ void BSPWalkCircle(float x, float y, float radiusSquared, const Callback &callba BSPNodeWalkCircle(level.HeadNode(), x, y, radiusSquared, callback); } -void gl_SetDynModelLight(AActor *self) +int gl_SetDynModelLight(AActor *self, int dynlightindex) { - // Legacy and deferred render paths gets the old flat model light - if (gl.lightmethod != LM_DIRECT) + // For deferred light mode this function gets called twice. First time for list upload, and second for draw. + if (gl.lightmethod == LM_DEFERRED && dynlightindex != -1) + { + gl_RenderState.SetDynLight(0, 0, 0); + modellightindex = dynlightindex; + return dynlightindex; + } + + // Legacy render path gets the old flat model light + if (gl.lightmethod == LM_LEGACY) { gl_SetDynSpriteLight(self, nullptr); - return; + return -1; } modellightdata.Clear(); @@ -249,6 +257,12 @@ void gl_SetDynModelLight(AActor *self) }); } - gl_RenderState.SetDynLight(0, 0, 0); - modellightindex = GLRenderer->mLights->UploadLights(modellightdata); + dynlightindex = GLRenderer->mLights->UploadLights(modellightdata); + + if (gl.lightmethod != LM_DEFERRED) + { + gl_RenderState.SetDynLight(0, 0, 0); + modellightindex = dynlightindex; + } + return dynlightindex; } diff --git a/src/gl/scene/gl_wall.h b/src/gl/scene/gl_wall.h index aa0bbaed4..5d041a55f 100644 --- a/src/gl/scene/gl_wall.h +++ b/src/gl/scene/gl_wall.h @@ -392,6 +392,8 @@ public: TArray *lightlist; DRotator Angles; + int dynlightindex; + void SplitSprite(sector_t * frontsector, bool translucent); void SetLowerParam(); void PerformSpriteClipAdjustment(AActor *thing, const DVector2 &thingpos, float spriteheight); @@ -422,6 +424,6 @@ inline float Dist2(float x1,float y1,float x2,float y2) void gl_SetDynSpriteLight(AActor *self, float x, float y, float z, subsector_t *subsec); void gl_SetDynSpriteLight(AActor *actor, particle_t *particle); -void gl_SetDynModelLight(AActor *self); +int gl_SetDynModelLight(AActor *self, int dynlightindex); #endif diff --git a/src/gl/scene/gl_weapon.cpp b/src/gl/scene/gl_weapon.cpp index 69551f28a..421517a26 100644 --- a/src/gl/scene/gl_weapon.cpp +++ b/src/gl/scene/gl_weapon.cpp @@ -183,6 +183,39 @@ static bool isBright(DPSprite *psp) return false; } +void GLSceneDrawer::SetupWeaponLight() +{ + weapondynlightindex.Clear(); + + AActor *camera = r_viewpoint.camera; + AActor * playermo = players[consoleplayer].camera; + player_t * player = playermo->player; + + // this is the same as in DrawPlayerSprites below + if (!player || + !r_drawplayersprites || + !camera->player || + (player->cheats & CF_CHASECAM) || + (r_deathcamera && camera->health <= 0)) + return; + + for (DPSprite *psp = player->psprites; psp != nullptr && psp->GetID() < PSP_TARGETCENTER; psp = psp->GetNext()) + { + if (psp->GetState() != nullptr) + { + // set the lighting parameters + if (gl_lights && GLRenderer->mLightCount && FixedColormap == CM_DEFAULT && gl_light_sprites) + { + FSpriteModelFrame *smf = playermo->player->ReadyWeapon ? gl_FindModelFrame(playermo->player->ReadyWeapon->GetClass(), psp->GetState()->sprite, psp->GetState()->GetFrame(), false) : nullptr; + if (smf) + { + weapondynlightindex[psp] = gl_SetDynModelLight(playermo, -1); + } + } + } + } +} + //========================================================================== // // R_DrawPlayerSprites @@ -355,7 +388,7 @@ void GLSceneDrawer::DrawPlayerSprites(sector_t * viewsector, bool hudModelStep) { // Todo: implement shader selection here RenderStyle = LegacyRenderStyles[STYLE_Translucent]; - OverrideShader = gl_fuzztype + 4; + OverrideShader = SHADER_NoTexture + gl_fuzztype; trans = 0.99f; // trans may not be 1 here } else @@ -422,7 +455,7 @@ void GLSceneDrawer::DrawPlayerSprites(sector_t * viewsector, bool hudModelStep) { FSpriteModelFrame *smf = playermo->player->ReadyWeapon ? gl_FindModelFrame(playermo->player->ReadyWeapon->GetClass(), psp->GetState()->sprite, psp->GetState()->GetFrame(), false) : nullptr; if (smf) - gl_SetDynModelLight(playermo); + gl_SetDynModelLight(playermo, weapondynlightindex[psp]); else gl_SetDynSpriteLight(playermo, NULL); } diff --git a/src/gl/shaders/gl_shader.cpp b/src/gl/shaders/gl_shader.cpp index 4af1ef5a1..141ee333a 100644 --- a/src/gl/shaders/gl_shader.cpp +++ b/src/gl/shaders/gl_shader.cpp @@ -52,20 +52,202 @@ #include "gl/textures/gl_material.h" #include "gl/dynlights/gl_lightbuffer.h" -//========================================================================== -// -// -// -//========================================================================== +static bool IsGlslWhitespace(char c) +{ + switch (c) + { + case ' ': + case '\r': + case '\n': + case '\t': + case '\f': + return true; + default: + return false; + } +} -bool FShader::Load(const char * name, const char * vert_prog_lump, const char * frag_prog_lump, const char * proc_prog_lump, const char * defines) +static FString NextGlslToken(const char *chars, long len, long &pos) +{ + // Eat whitespace + long tokenStart = pos; + while (tokenStart != len && IsGlslWhitespace(chars[tokenStart])) + tokenStart++; + + // Find token end + long tokenEnd = tokenStart; + while (tokenEnd != len && !IsGlslWhitespace(chars[tokenEnd]) && chars[tokenEnd] != ';') + tokenEnd++; + + pos = tokenEnd; + return FString(chars + tokenStart, tokenEnd - tokenStart); +} + +static FString RemoveLegacyUserUniforms(FString code) +{ + // User shaders must declare their uniforms via the GLDEFS file. + // The following code searches for legacy uniform declarations in the shader itself and replaces them with whitespace. + + long len = (long)code.Len(); + char *chars = code.LockBuffer(); + + long startIndex = 0; + while (true) + { + long matchIndex = code.IndexOf("uniform", startIndex); + if (matchIndex == -1) + break; + + bool isLegacyUniformName = false; + + bool isKeywordStart = matchIndex == 0 || IsGlslWhitespace(chars[matchIndex - 1]); + bool isKeywordEnd = matchIndex + 7 == len || IsGlslWhitespace(chars[matchIndex + 7]); + if (isKeywordStart && isKeywordEnd) + { + long pos = matchIndex + 7; + FString type = NextGlslToken(chars, len, pos); + FString identifier = NextGlslToken(chars, len, pos); + + isLegacyUniformName = type.Compare("float") == 0 && identifier.Compare("timer") == 0; + } + + if (isLegacyUniformName) + { + long statementEndIndex = code.IndexOf(';', matchIndex + 7); + if (statementEndIndex == -1) + statementEndIndex = len; + for (long i = matchIndex; i <= statementEndIndex; i++) + { + if (!IsGlslWhitespace(chars[i])) + chars[i] = ' '; + } + startIndex = statementEndIndex; + } + else + { + startIndex = matchIndex + 7; + } + } + + code.UnlockBuffer(); + + return code; +} + +bool FShader::Load(const char * name, const char * vert_prog_lump, const char * frag_prog_lump, const char * proc_prog_lump, const char * light_fragprog, const char * defines) { static char buffer[10000]; FString error; - int i_lump = Wads.CheckNumForFullName("shaders/glsl/shaderdefs.i", 0); - if (i_lump == -1) I_Error("Unable to load 'shaders/glsl/shaderdefs.i'"); - FMemLump i_data = Wads.ReadLump(i_lump); + FString i_data; + + // these settings are actually pointless but there seem to be some old ATI drivers that fail to compile the shader without setting the precision here. + i_data += "precision highp int;\n"; + i_data += "precision highp float;\n"; + + i_data += "uniform vec4 uCameraPos;\n"; + i_data += "uniform int uTextureMode;\n"; + i_data += "uniform float uClipHeight;\n"; + i_data += "uniform float uClipHeightDirection;\n"; + i_data += "uniform vec2 uClipSplit;\n"; + i_data += "uniform vec4 uClipLine;\n"; + i_data += "uniform float uAlphaThreshold;\n"; + + // colors + i_data += "uniform vec4 uObjectColor;\n"; + i_data += "uniform vec4 uObjectColor2;\n"; + i_data += "uniform vec4 uDynLightColor;\n"; + i_data += "uniform vec4 uFogColor;\n"; + i_data += "uniform float uDesaturationFactor;\n"; + i_data += "uniform float uInterpolationFactor;\n"; + + // Fixed colormap stuff + i_data += "uniform int uFixedColormap;\n"; // 0, when no fixed colormap, 1 for a light value, 2 for a color blend, 3 for a fog layer + i_data += "uniform vec4 uFixedColormapStart;\n"; + i_data += "uniform vec4 uFixedColormapRange;\n"; + + // Glowing walls stuff + i_data += "uniform vec4 uGlowTopPlane;\n"; + i_data += "uniform vec4 uGlowTopColor;\n"; + i_data += "uniform vec4 uGlowBottomPlane;\n"; + i_data += "uniform vec4 uGlowBottomColor;\n"; + + i_data += "uniform vec4 uSplitTopPlane;\n"; + i_data += "uniform vec4 uSplitBottomPlane;\n"; + + // Lighting + Fog + i_data += "uniform vec4 uLightAttr;\n"; + i_data += "#define uLightLevel uLightAttr.a\n"; + i_data += "#define uFogDensity uLightAttr.b\n"; + i_data += "#define uLightFactor uLightAttr.g\n"; + i_data += "#define uLightDist uLightAttr.r\n"; + i_data += "uniform int uFogEnabled;\n"; + i_data += "uniform int uPalLightLevels;\n"; + i_data += "uniform float uGlobVis;\n"; // uGlobVis = R_GetGlobVis(r_visibility) / 32.0 + + // dynamic lights + i_data += "uniform int uLightIndex;\n"; + + // Software fuzz scaling + i_data += "uniform int uViewHeight;\n"; + + // Blinn glossiness and specular level + i_data += "uniform vec2 uSpecularMaterial;\n"; + + // quad drawer stuff + i_data += "#ifdef USE_QUAD_DRAWER\n"; + i_data += "uniform mat4 uQuadVertices;\n"; + i_data += "uniform mat4 uQuadTexCoords;\n"; + i_data += "uniform int uQuadMode;\n"; + i_data += "#endif\n"; + + // matrices + i_data += "uniform mat4 ProjectionMatrix;\n"; + i_data += "uniform mat4 ViewMatrix;\n"; + i_data += "uniform mat4 ModelMatrix;\n"; + i_data += "uniform mat4 NormalViewMatrix;\n"; + i_data += "uniform mat4 NormalModelMatrix;\n"; + i_data += "uniform mat4 TextureMatrix;\n"; + + // light buffers + i_data += "#ifdef SHADER_STORAGE_LIGHTS\n"; + i_data += "layout(std430, binding = 1) buffer LightBufferSSO\n"; + i_data += "{\n"; + i_data += " vec4 lights[];\n"; + i_data += "};\n"; + i_data += "#elif defined NUM_UBO_LIGHTS\n"; + i_data += "uniform LightBufferUBO\n"; + i_data += "{\n"; + i_data += " vec4 lights[NUM_UBO_LIGHTS];\n"; + i_data += "};\n"; + i_data += "#endif\n"; + + // textures + i_data += "uniform sampler2D tex;\n"; + i_data += "uniform sampler2D ShadowMap;\n"; + i_data += "uniform sampler2D texture2;\n"; + i_data += "uniform sampler2D texture3;\n"; + i_data += "uniform sampler2D texture4;\n"; + i_data += "uniform sampler2D texture5;\n"; + i_data += "uniform sampler2D texture6;\n"; + + // timer data + i_data += "uniform float timer;\n"; // To do: we must search user shaders for this declaration and remove it + + // material types + i_data += "#if defined(SPECULAR)\n"; + i_data += "#define normaltexture texture2\n"; + i_data += "#define speculartexture texture3\n"; + i_data += "#define brighttexture texture4\n"; + i_data += "#elif defined(PBR)\n"; + i_data += "#define normaltexture texture2\n"; + i_data += "#define metallictexture texture3\n"; + i_data += "#define roughnesstexture texture4\n"; + i_data += "#define aotexture texture5\n"; + i_data += "#define brighttexture texture6\n"; + i_data += "#else\n"; + i_data += "#define brighttexture texture2\n"; + i_data += "#endif\n"; int vp_lump = Wads.CheckNumForFullName(vert_prog_lump, 0); if (vp_lump == -1) I_Error("Unable to load '%s'", vert_prog_lump); @@ -120,14 +302,19 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char * vp_comb << "#define SUPPORTS_SHADOWMAPS\n"; } - vp_comb << defines << i_data.GetString().GetChars(); + vp_comb << defines << i_data.GetChars(); FString fp_comb = vp_comb; + vp_comb << "#line 1\n"; + fp_comb << "#line 1\n"; + vp_comb << vp_data.GetString().GetChars() << "\n"; fp_comb << fp_data.GetString().GetChars() << "\n"; if (proc_prog_lump != NULL) { + fp_comb << "#line 1\n"; + if (*proc_prog_lump != '#') { int pp_lump = Wads.CheckNumForFullName(proc_prog_lump); @@ -141,7 +328,7 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char * fp_comb.Substitute("vec4 frag = ProcessTexel();", "vec4 frag = Process(vec4(1.0));"); } - fp_comb << pp_data.GetString().GetChars(); + fp_comb << RemoveLegacyUserUniforms(pp_data.GetString()).GetChars(); fp_comb.Substitute("gl_TexCoord[0]", "vTexCoord"); // fix old custom shaders. if (pp_data.GetString().IndexOf("ProcessLight") < 0) @@ -159,6 +346,14 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char * } } + if (light_fragprog) + { + int pp_lump = Wads.CheckNumForFullName(light_fragprog); + if (pp_lump == -1) I_Error("Unable to load '%s'", light_fragprog); + FMemLump pp_data = Wads.ReadLump(pp_lump); + fp_comb << pp_data.GetString().GetChars() << "\n"; + } + if (gl.flags & RFL_NO_CLIP_PLANES) { // On ATI's GL3 drivers we have to disable gl_ClipDistance because it's hopelessly broken. @@ -254,6 +449,7 @@ bool FShader::Load(const char * name, const char * vert_prog_lump, const char * muClipHeight.Init(hShader, "uClipHeight"); muClipHeightDirection.Init(hShader, "uClipHeightDirection"); muAlphaThreshold.Init(hShader, "uAlphaThreshold"); + muSpecularMaterial.Init(hShader, "uSpecularMaterial"); muViewHeight.Init(hShader, "uViewHeight"); muTimer.Init(hShader, "timer"); @@ -326,9 +522,10 @@ bool FShader::Bind() // //========================================================================== -FShader *FShaderCollection::Compile (const char *ShaderName, const char *ShaderPath, bool usediscard, EPassType passType) +FShader *FShaderCollection::Compile (const char *ShaderName, const char *ShaderPath, const char *LightModePath, const char *shaderdefines, bool usediscard, EPassType passType) { FString defines; + defines += shaderdefines; // this can't be in the shader code due to ATI strangeness. if (gl.MaxLights() == 128) defines += "#define MAXLIGHTS128\n"; if (!usediscard) defines += "#define NO_ALPHATEST\n"; @@ -338,7 +535,7 @@ FShader *FShaderCollection::Compile (const char *ShaderName, const char *ShaderP try { shader = new FShader(ShaderName); - if (!shader->Load(ShaderName, "shaders/glsl/main.vp", "shaders/glsl/main.fp", ShaderPath, defines.GetChars())) + if (!shader->Load(ShaderName, "shaders/glsl/main.vp", "shaders/glsl/main.fp", ShaderPath, LightModePath, defines.GetChars())) { I_FatalError("Unable to load shader %s\n", ShaderName); } @@ -375,26 +572,31 @@ struct FDefaultShader { const char * ShaderName; const char * gettexelfunc; + const char * lightfunc; + const char * Defines; }; -// Note: the FIRST_USER_SHADER constant in gl_shader.h needs -// to be updated whenever the size of this array is modified. +// Note: the MaterialShaderIndex enum in gl_shader.h needs to be updated whenever this array is modified. static const FDefaultShader defaultshaders[]= { - {"Default", "shaders/glsl/func_normal.fp"}, - {"Warp 1", "shaders/glsl/func_warp1.fp"}, - {"Warp 2", "shaders/glsl/func_warp2.fp"}, - {"Brightmap","shaders/glsl/func_brightmap.fp"}, - {"No Texture", "shaders/glsl/func_notexture.fp"}, - {"Basic Fuzz", "shaders/glsl/fuzz_standard.fp"}, - {"Smooth Fuzz", "shaders/glsl/fuzz_smooth.fp"}, - {"Swirly Fuzz", "shaders/glsl/fuzz_swirly.fp"}, - {"Translucent Fuzz", "shaders/glsl/fuzz_smoothtranslucent.fp"}, - {"Jagged Fuzz", "shaders/glsl/fuzz_jagged.fp"}, - {"Noise Fuzz", "shaders/glsl/fuzz_noise.fp"}, - {"Smooth Noise Fuzz", "shaders/glsl/fuzz_smoothnoise.fp"}, - {"Software Fuzz", "shaders/glsl/fuzz_software.fp"}, - {NULL,NULL} + {"Default", "shaders/glsl/func_normal.fp", "shaders/glsl/material_normal.fp", ""}, + {"Warp 1", "shaders/glsl/func_warp1.fp", "shaders/glsl/material_normal.fp", ""}, + {"Warp 2", "shaders/glsl/func_warp2.fp", "shaders/glsl/material_normal.fp", ""}, + {"Brightmap","shaders/glsl/func_brightmap.fp", "shaders/glsl/material_normal.fp", ""}, + {"Specular", "shaders/glsl/func_normal.fp", "shaders/glsl/material_specular.fp", "#define SPECULAR\n#define NORMALMAP\n"}, + {"SpecularBrightmap", "shaders/glsl/func_brightmap.fp", "shaders/glsl/material_specular.fp", "#define SPECULAR\n#define NORMALMAP\n"}, + {"PBR","shaders/glsl/func_normal.fp", "shaders/glsl/material_pbr.fp", "#define PBR\n#define NORMALMAP\n"}, + {"PBRBrightmap","shaders/glsl/func_brightmap.fp", "shaders/glsl/material_pbr.fp", "#define PBR\n#define NORMALMAP\n"}, + {"No Texture", "shaders/glsl/func_notexture.fp", "shaders/glsl/material_normal.fp", ""}, + {"Basic Fuzz", "shaders/glsl/fuzz_standard.fp", "shaders/glsl/material_normal.fp", ""}, + {"Smooth Fuzz", "shaders/glsl/fuzz_smooth.fp", "shaders/glsl/material_normal.fp", ""}, + {"Swirly Fuzz", "shaders/glsl/fuzz_swirly.fp", "shaders/glsl/material_normal.fp", ""}, + {"Translucent Fuzz", "shaders/glsl/fuzz_smoothtranslucent.fp", "shaders/glsl/material_normal.fp", ""}, + {"Jagged Fuzz", "shaders/glsl/fuzz_jagged.fp", "shaders/glsl/material_normal.fp", ""}, + {"Noise Fuzz", "shaders/glsl/fuzz_noise.fp", "shaders/glsl/material_normal.fp", ""}, + {"Smooth Noise Fuzz", "shaders/glsl/fuzz_smoothnoise.fp", "shaders/glsl/material_normal.fp", ""}, + {"Software Fuzz", "shaders/glsl/fuzz_software.fp", "shaders/glsl/material_normal.fp", ""}, + {nullptr,nullptr,nullptr,nullptr} }; static TArray usershaders; @@ -405,15 +607,16 @@ struct FEffectShader const char *vp; const char *fp1; const char *fp2; + const char *fp3; const char *defines; }; static const FEffectShader effectshaders[]= { - { "fogboundary", "shaders/glsl/main.vp", "shaders/glsl/fogboundary.fp", NULL, "#define NO_ALPHATEST\n" }, - { "spheremap", "shaders/glsl/main.vp", "shaders/glsl/main.fp", "shaders/glsl/func_normal.fp", "#define SPHEREMAP\n#define NO_ALPHATEST\n" }, - { "burn", "shaders/glsl/main.vp", "shaders/glsl/burn.fp", NULL, "#define SIMPLE\n#define NO_ALPHATEST\n" }, - { "stencil", "shaders/glsl/main.vp", "shaders/glsl/stencil.fp", NULL, "#define SIMPLE\n#define NO_ALPHATEST\n" }, + { "fogboundary", "shaders/glsl/main.vp", "shaders/glsl/fogboundary.fp", nullptr, nullptr, "#define NO_ALPHATEST\n" }, + { "spheremap", "shaders/glsl/main.vp", "shaders/glsl/main.fp", "shaders/glsl/func_normal.fp", "shaders/glsl/material_normal.fp", "#define SPHEREMAP\n#define NO_ALPHATEST\n" }, + { "burn", "shaders/glsl/main.vp", "shaders/glsl/burn.fp", nullptr, nullptr, "#define SIMPLE\n#define NO_ALPHATEST\n" }, + { "stencil", "shaders/glsl/main.vp", "shaders/glsl/stencil.fp", nullptr, nullptr, "#define SIMPLE\n#define NO_ALPHATEST\n" }, }; FShaderManager::FShaderManager() @@ -524,8 +727,8 @@ FShaderCollection::~FShaderCollection() void FShaderCollection::CompileShaders(EPassType passType) { - mTextureEffects.Clear(); - mTextureEffectsNAT.Clear(); + mMaterialShaders.Clear(); + mMaterialShadersNAT.Clear(); for (int i = 0; i < MAX_EFFECTS; i++) { mEffectShaders[i] = NULL; @@ -533,12 +736,12 @@ void FShaderCollection::CompileShaders(EPassType passType) for(int i=0;defaultshaders[i].ShaderName != NULL;i++) { - FShader *shc = Compile(defaultshaders[i].ShaderName, defaultshaders[i].gettexelfunc, true, passType); - mTextureEffects.Push(shc); - if (i <= 3) + FShader *shc = Compile(defaultshaders[i].ShaderName, defaultshaders[i].gettexelfunc, defaultshaders[i].lightfunc, defaultshaders[i].Defines, true, passType); + mMaterialShaders.Push(shc); + if (i < SHADER_NoTexture) { - FShader *shc = Compile(defaultshaders[i].ShaderName, defaultshaders[i].gettexelfunc, false, passType); - mTextureEffectsNAT.Push(shc); + FShader *shc = Compile(defaultshaders[i].ShaderName, defaultshaders[i].gettexelfunc, defaultshaders[i].lightfunc, defaultshaders[i].Defines, false, passType); + mMaterialShadersNAT.Push(shc); } } @@ -547,15 +750,15 @@ void FShaderCollection::CompileShaders(EPassType passType) FString name = ExtractFileBase(usershaders[i]); FName sfn = name; - FShader *shc = Compile(sfn, usershaders[i], true, passType); - mTextureEffects.Push(shc); + FShader *shc = Compile(sfn, usershaders[i], "shaders/glsl/material_normal.fp", "", true, passType); + mMaterialShaders.Push(shc); } for(int i=0;iLoad(effectshaders[i].ShaderName, effectshaders[i].vp, effectshaders[i].fp1, - effectshaders[i].fp2, effectshaders[i].defines)) + effectshaders[i].fp2, effectshaders[i].fp3, effectshaders[i].defines)) { delete eff; } @@ -571,21 +774,21 @@ void FShaderCollection::CompileShaders(EPassType passType) void FShaderCollection::Clean() { - for (unsigned int i = 0; i < mTextureEffectsNAT.Size(); i++) + for (unsigned int i = 0; i < mMaterialShadersNAT.Size(); i++) { - if (mTextureEffectsNAT[i] != NULL) delete mTextureEffectsNAT[i]; + if (mMaterialShadersNAT[i] != NULL) delete mMaterialShadersNAT[i]; } - for (unsigned int i = 0; i < mTextureEffects.Size(); i++) + for (unsigned int i = 0; i < mMaterialShaders.Size(); i++) { - if (mTextureEffects[i] != NULL) delete mTextureEffects[i]; + if (mMaterialShaders[i] != NULL) delete mMaterialShaders[i]; } for (int i = 0; i < MAX_EFFECTS; i++) { if (mEffectShaders[i] != NULL) delete mEffectShaders[i]; mEffectShaders[i] = NULL; } - mTextureEffects.Clear(); - mTextureEffectsNAT.Clear(); + mMaterialShaders.Clear(); + mMaterialShadersNAT.Clear(); } //========================================================================== @@ -598,9 +801,9 @@ int FShaderCollection::Find(const char * shn) { FName sfn = shn; - for(unsigned int i=0;imName == sfn) + if (mMaterialShaders[i]->mName == sfn) { return i; } @@ -638,19 +841,19 @@ void FShaderCollection::ApplyMatrices(VSMatrix *proj, VSMatrix *view) VSMatrix norm; norm.computeNormalMatrix(*view); - for (int i = 0; i < 4; i++) + for (int i = 0; i < SHADER_NoTexture; i++) { - mTextureEffects[i]->ApplyMatrices(proj, view, &norm); - mTextureEffectsNAT[i]->ApplyMatrices(proj, view, &norm); + mMaterialShaders[i]->ApplyMatrices(proj, view, &norm); + mMaterialShadersNAT[i]->ApplyMatrices(proj, view, &norm); } - mTextureEffects[4]->ApplyMatrices(proj, view, &norm); + mMaterialShaders[SHADER_NoTexture]->ApplyMatrices(proj, view, &norm); if (gl_fuzztype != 0) { - mTextureEffects[4 + gl_fuzztype]->ApplyMatrices(proj, view, &norm); + mMaterialShaders[SHADER_NoTexture + gl_fuzztype]->ApplyMatrices(proj, view, &norm); } - for (unsigned i = 12; i < mTextureEffects.Size(); i++) + for (unsigned i = FIRST_USER_SHADER; i < mMaterialShaders.Size(); i++) { - mTextureEffects[i]->ApplyMatrices(proj, view, &norm); + mMaterialShaders[i]->ApplyMatrices(proj, view, &norm); } for (int i = 0; i < MAX_EFFECTS; i++) { diff --git a/src/gl/shaders/gl_shader.h b/src/gl/shaders/gl_shader.h index 6265d62a9..8b119aec9 100644 --- a/src/gl/shaders/gl_shader.h +++ b/src/gl/shaders/gl_shader.h @@ -285,6 +285,7 @@ class FShader FBufferedUniform1f muClipHeightDirection; FBufferedUniform1f muAlphaThreshold; FBufferedUniform1i muViewHeight; + FBufferedUniform2f muSpecularMaterial; FBufferedUniform1f muTimer; int lights_index; @@ -316,7 +317,7 @@ public: ~FShader(); - bool Load(const char * name, const char * vert_prog_lump, const char * fragprog, const char * fragprog2, const char *defines); + bool Load(const char * name, const char * vert_prog_lump, const char * fragprog, const char * fragprog2, const char * light_fragprog, const char *defines); void SetColormapColor(float r, float g, float b, float r1, float g1, float b1); void SetGlowParams(float *topcolors, float topheight, float *bottomcolors, float bottomheight); @@ -356,8 +357,8 @@ private: class FShaderCollection { - TArray mTextureEffects; - TArray mTextureEffectsNAT; + TArray mMaterialShaders; + TArray mMaterialShadersNAT; FShader *mEffectShaders[MAX_EFFECTS]; void Clean(); @@ -366,20 +367,20 @@ class FShaderCollection public: FShaderCollection(EPassType passType); ~FShaderCollection(); - FShader *Compile(const char *ShaderName, const char *ShaderPath, bool usediscard, EPassType passType); + FShader *Compile(const char *ShaderName, const char *ShaderPath, const char *LightModePath, const char *shaderdefines, bool usediscard, EPassType passType); int Find(const char *mame); FShader *BindEffect(int effect); void ApplyMatrices(VSMatrix *proj, VSMatrix *view); void ResetFixedColormap() { - for (unsigned i = 0; i < mTextureEffects.Size(); i++) + for (unsigned i = 0; i < mMaterialShaders.Size(); i++) { - mTextureEffects[i]->currentfixedcolormap = -1; + mMaterialShaders[i]->currentfixedcolormap = -1; } - for (unsigned i = 0; i < mTextureEffectsNAT.Size(); i++) + for (unsigned i = 0; i < mMaterialShadersNAT.Size(); i++) { - mTextureEffectsNAT[i]->currentfixedcolormap = -1; + mMaterialShadersNAT[i]->currentfixedcolormap = -1; } } @@ -388,17 +389,37 @@ public: // indices 0-2 match the warping modes, 3 is brightmap, 4 no texture, the following are custom if (!alphateston && eff <= 3) { - return mTextureEffectsNAT[eff]; // Non-alphatest shaders are only created for default, warp1+2 and brightmap. The rest won't get used anyway + return mMaterialShadersNAT[eff]; // Non-alphatest shaders are only created for default, warp1+2 and brightmap. The rest won't get used anyway } - if (eff < mTextureEffects.Size()) + if (eff < mMaterialShaders.Size()) { - return mTextureEffects[eff]; + return mMaterialShaders[eff]; } return NULL; } }; -#define FIRST_USER_SHADER 13 +enum MaterialShaderIndex +{ + SHADER_Default, + SHADER_Warp1, + SHADER_Warp2, + SHADER_Brightmap, + SHADER_Specular, + SHADER_SpecularBrightmap, + SHADER_PBR, + SHADER_PBRBrightmap, + SHADER_NoTexture, + SHADER_BasicFuzz, + SHADER_SmoothFuzz, + SHADER_SwirlyFuzz, + SHADER_TranslucentFuzz, + SHADER_JaggedFuzz, + SHADER_NoiseFuzz, + SHADER_SmoothNoiseFuzz, + SHADER_SoftwareFuzz, + FIRST_USER_SHADER +}; enum { diff --git a/src/gl/textures/gl_material.cpp b/src/gl/textures/gl_material.cpp index 53d22f857..859706772 100644 --- a/src/gl/textures/gl_material.cpp +++ b/src/gl/textures/gl_material.cpp @@ -438,7 +438,7 @@ int FMaterial::mMaxBound; FMaterial::FMaterial(FTexture * tx, bool expanded) { - mShaderIndex = 0; + mShaderIndex = SHADER_Default; tex = tx; // TODO: apply custom shader object here @@ -449,7 +449,7 @@ FMaterial::FMaterial(FTexture * tx, bool expanded) */ if (tx->bWarped) { - mShaderIndex = tx->bWarped; + mShaderIndex = tx->bWarped; // This picks SHADER_Warp1 or SHADER_Warp2 tx->gl_info.shaderspeed = static_cast(tx)->GetSpeed(); } else if (tx->bHasCanvas) @@ -468,13 +468,37 @@ FMaterial::FMaterial(FTexture * tx, bool expanded) } else { + if (tx->gl_info.Normal && tx->gl_info.Specular) + { + for (auto &texture : { tx->gl_info.Normal, tx->gl_info.Specular }) + { + ValidateSysTexture(texture, expanded); + mTextureLayers.Push({ texture, false }); + } + mShaderIndex = SHADER_Specular; + } + else if (tx->gl_info.Normal && tx->gl_info.Metallic && tx->gl_info.Roughness && tx->gl_info.AmbientOcclusion) + { + for (auto &texture : { tx->gl_info.Normal, tx->gl_info.Metallic, tx->gl_info.Roughness, tx->gl_info.AmbientOcclusion }) + { + ValidateSysTexture(texture, expanded); + mTextureLayers.Push({ texture, false }); + } + mShaderIndex = SHADER_PBR; + } + tx->CreateDefaultBrightmap(); if (tx->gl_info.Brightmap != NULL) { ValidateSysTexture(tx->gl_info.Brightmap, expanded); FTextureLayer layer = {tx->gl_info.Brightmap, false}; mTextureLayers.Push(layer); - mShaderIndex = 3; + if (mShaderIndex == SHADER_Specular) + mShaderIndex = SHADER_SpecularBrightmap; + else if (mShaderIndex == SHADER_PBR) + mShaderIndex = SHADER_PBRBrightmap; + else + mShaderIndex = SHADER_Brightmap; } } } @@ -804,7 +828,7 @@ void FMaterial::GetTexCoordInfo(FTexCoordInfo *tci, float x, float y) const int FMaterial::GetAreas(FloatRect **pAreas) const { - if (mShaderIndex == 0) // texture splitting can only be done if there's no attached effects + if (mShaderIndex == SHADER_Default) // texture splitting can only be done if there's no attached effects { FTexture *tex = mBaseLayer->tex; *pAreas = tex->gl_info.areas; diff --git a/src/gl/textures/gl_texture.cpp b/src/gl/textures/gl_texture.cpp index ddfd7ecc9..0d3e84e4b 100644 --- a/src/gl/textures/gl_texture.cpp +++ b/src/gl/textures/gl_texture.cpp @@ -191,10 +191,17 @@ FTexture::MiscGLInfo::MiscGLInfo() throw() mIsTransparent = -1; shaderspeed = 1.f; shaderindex = 0; + Glossiness = 10.0f; + SpecularLevel = 0.1f; Material[1] = Material[0] = NULL; SystemTexture[1] = SystemTexture[0] = NULL; Brightmap = NULL; + Normal = NULL; + Specular = NULL; + Metallic = NULL; + Roughness = NULL; + AmbientOcclusion = NULL; } FTexture::MiscGLInfo::~MiscGLInfo() @@ -542,6 +549,124 @@ int FBrightmapTexture::CopyTrueColorPixels(FBitmap *bmp, int x, int y, int rotat return 0; } +//========================================================================== +// +// Parses a material definition +// +//========================================================================== + +void gl_ParseMaterial(FScanner &sc, int deflump) +{ + int type = FTexture::TEX_Any; + bool disable_fullbright = false; + bool disable_fullbright_specified = false; + bool thiswad = false; + bool iwad = false; + + FTexture *textures[6]; + const char *keywords[7] = { "brightmap", "normal", "specular", "metallic", "roughness", "ao", nullptr }; + const char *notFound[6] = { "Brightmap", "Normalmap", "Specular texture", "Metallic texture", "Roughness texture", "Ambient occlusion texture" }; + memset(textures, 0, sizeof(textures)); + + sc.MustGetString(); + if (sc.Compare("texture")) type = FTexture::TEX_Wall; + else if (sc.Compare("flat")) type = FTexture::TEX_Flat; + else if (sc.Compare("sprite")) type = FTexture::TEX_Sprite; + else sc.UnGet(); + + sc.MustGetString(); + FTextureID no = TexMan.CheckForTexture(sc.String, type, FTextureManager::TEXMAN_TryAny | FTextureManager::TEXMAN_Overridable); + FTexture *tex = TexMan[no]; + + sc.MustGetToken('{'); + while (!sc.CheckToken('}')) + { + sc.MustGetString(); + if (sc.Compare("disablefullbright")) + { + // This can also be used without a brightness map to disable + // fullbright in rotations that only use brightness maps on + // other angles. + disable_fullbright = true; + disable_fullbright_specified = true; + } + else if (sc.Compare("thiswad")) + { + // only affects textures defined in the WAD containing the definition file. + thiswad = true; + } + else if (sc.Compare ("iwad")) + { + // only affects textures defined in the IWAD. + iwad = true; + } + else if (sc.Compare("glossiness")) + { + sc.MustGetFloat(); + if (tex) + tex->gl_info.Glossiness = sc.Float; + } + else if (sc.Compare("specularlevel")) + { + sc.MustGetFloat(); + if (tex) + tex->gl_info.SpecularLevel = sc.Float; + } + else + { + for (int i = 0; keywords[i] != nullptr; i++) + { + if (sc.Compare (keywords[i])) + { + sc.MustGetString(); + if (textures[i]) + Printf("Multiple %s definitions in texture %s\n", keywords[i], tex? tex->Name.GetChars() : "(null)"); + textures[i] = TexMan.FindTexture(sc.String, FTexture::TEX_Any, FTextureManager::TEXMAN_TryAny); + if (!textures[i]) + Printf("%s '%s' not found in texture '%s'\n", notFound[i], sc.String, tex? tex->Name.GetChars() : "(null)"); + break; + } + } + } + } + if (!tex) + { + return; + } + if (thiswad || iwad) + { + bool useme = false; + int lumpnum = tex->GetSourceLump(); + + if (lumpnum != -1) + { + if (iwad && Wads.GetLumpFile(lumpnum) <= Wads.GetIwadNum()) useme = true; + if (thiswad && Wads.GetLumpFile(lumpnum) == deflump) useme = true; + } + if (!useme) return; + } + + FTexture **bindings[6] = + { + &tex->gl_info.Brightmap, + &tex->gl_info.Normal, + &tex->gl_info.Specular, + &tex->gl_info.Metallic, + &tex->gl_info.Roughness, + &tex->gl_info.AmbientOcclusion + }; + for (int i = 0; keywords[i] != nullptr; i++) + { + if (textures[i]) + { + textures[i]->bMasked = false; + *bindings[i] = textures[i]; + } + } + + if (disable_fullbright_specified) + tex->gl_info.bDisableFullbright = disable_fullbright; +} //========================================================================== // @@ -638,26 +763,51 @@ void gl_ParseBrightmap(FScanner &sc, int deflump) //========================================================================== // -// +// Search auto paths for extra material textures // //========================================================================== -void AddAutoBrightmaps() +struct AutoTextureSearchPath +{ + const char *path; + ptrdiff_t offset; + + void SetTexture(FTexture *material, FTexture *texture) const + { + *reinterpret_cast(reinterpret_cast(&material->gl_info) + offset) = texture; + } +}; + +static AutoTextureSearchPath autosearchpaths[] = +{ + { "brightmaps/auto/", offsetof(FTexture::MiscGLInfo, Brightmap) }, // For backwards compatibility + { "materials/brightmaps/auto/", offsetof(FTexture::MiscGLInfo, Brightmap) }, + { "materials/normalmaps/auto/", offsetof(FTexture::MiscGLInfo, Normal) }, + { "materials/specular/auto/", offsetof(FTexture::MiscGLInfo, Specular) }, + { "materials/metallic/auto/", offsetof(FTexture::MiscGLInfo, Metallic) }, + { "materials/roughness/auto/", offsetof(FTexture::MiscGLInfo, Roughness) }, + { "materials/ao/auto/", offsetof(FTexture::MiscGLInfo, AmbientOcclusion) } +}; + +void AddAutoMaterials() { int num = Wads.GetNumLumps(); for (int i = 0; i < num; i++) { const char *name = Wads.GetLumpFullName(i); - if (strstr(name, "brightmaps/auto/") == name) + for (const AutoTextureSearchPath &searchpath : autosearchpaths) { - TArray list; - FString texname = ExtractFileBase(name, false); - TexMan.ListTextures(texname, list); - auto bmtex = TexMan.FindTexture(name, FTexture::TEX_Any, FTextureManager::TEXMAN_TryAny); - for (auto texid : list) + if (strstr(name, searchpath.path) == name) { - bmtex->bMasked = false; - TexMan[texid]->gl_info.Brightmap = bmtex; + TArray list; + FString texname = ExtractFileBase(name, false); + TexMan.ListTextures(texname, list); + auto bmtex = TexMan.FindTexture(name, FTexture::TEX_Any, FTextureManager::TEXMAN_TryAny); + for (auto texid : list) + { + bmtex->bMasked = false; + searchpath.SetTexture(TexMan[texid], bmtex); + } } } } diff --git a/src/textures/textures.h b/src/textures/textures.h index 539d38c43..c80abf87b 100644 --- a/src/textures/textures.h +++ b/src/textures/textures.h @@ -328,6 +328,11 @@ protected: Rotations = other->Rotations; gl_info = other->gl_info; gl_info.Brightmap = NULL; + gl_info.Normal = NULL; + gl_info.Specular = NULL; + gl_info.Metallic = NULL; + gl_info.Roughness = NULL; + gl_info.AmbientOcclusion = NULL; gl_info.areas = NULL; } @@ -362,6 +367,13 @@ public: FMaterial *Material[2]; FGLTexture *SystemTexture[2]; FTexture *Brightmap; + FTexture *Normal; // Normal map texture + FTexture *Specular; // Specular light texture for the diffuse+normal+specular light model + FTexture *Metallic; // Metalness texture for the physically based rendering (PBR) light model + FTexture *Roughness; // Roughness texture for PBR + FTexture *AmbientOcclusion; // Ambient occlusion texture for PBR + float Glossiness; + float SpecularLevel; PalEntry GlowColor; int GlowHeight; FloatRect *areas; diff --git a/wadsrc/static/shaders/glsl/burn.fp b/wadsrc/static/shaders/glsl/burn.fp index f928fd8db..d3f6f20e9 100644 --- a/wadsrc/static/shaders/glsl/burn.fp +++ b/wadsrc/static/shaders/glsl/burn.fp @@ -1,5 +1,4 @@ -uniform sampler2D tex; -uniform sampler2D texture2; + in vec4 vTexCoord; in vec4 vColor; out vec4 FragColor; diff --git a/wadsrc/static/shaders/glsl/func_brightmap.fp b/wadsrc/static/shaders/glsl/func_brightmap.fp index 8e5f44d04..7f92d36e5 100644 --- a/wadsrc/static/shaders/glsl/func_brightmap.fp +++ b/wadsrc/static/shaders/glsl/func_brightmap.fp @@ -1,4 +1,3 @@ -uniform sampler2D texture2; vec4 ProcessTexel() { @@ -7,6 +6,6 @@ vec4 ProcessTexel() vec4 ProcessLight(vec4 color) { - vec4 brightpix = desaturate(texture(texture2, vTexCoord.st)); + vec4 brightpix = desaturate(texture(brighttexture, vTexCoord.st)); return vec4(min (color.rgb + brightpix.rgb, 1.0), color.a); } diff --git a/wadsrc/static/shaders/glsl/func_warp1.fp b/wadsrc/static/shaders/glsl/func_warp1.fp index 891eaa936..4214f771f 100644 --- a/wadsrc/static/shaders/glsl/func_warp1.fp +++ b/wadsrc/static/shaders/glsl/func_warp1.fp @@ -1,4 +1,3 @@ -uniform float timer; vec4 ProcessTexel() { diff --git a/wadsrc/static/shaders/glsl/func_warp2.fp b/wadsrc/static/shaders/glsl/func_warp2.fp index ee712593d..389fad9df 100644 --- a/wadsrc/static/shaders/glsl/func_warp2.fp +++ b/wadsrc/static/shaders/glsl/func_warp2.fp @@ -1,4 +1,3 @@ -uniform float timer; vec4 ProcessTexel() { diff --git a/wadsrc/static/shaders/glsl/func_warp3.fp b/wadsrc/static/shaders/glsl/func_warp3.fp index a81969ca6..dceca2477 100644 --- a/wadsrc/static/shaders/glsl/func_warp3.fp +++ b/wadsrc/static/shaders/glsl/func_warp3.fp @@ -1,4 +1,3 @@ -uniform float timer; vec4 ProcessTexel() { diff --git a/wadsrc/static/shaders/glsl/func_wavex.fp b/wadsrc/static/shaders/glsl/func_wavex.fp index e4230ae87..05ab53028 100644 --- a/wadsrc/static/shaders/glsl/func_wavex.fp +++ b/wadsrc/static/shaders/glsl/func_wavex.fp @@ -1,4 +1,3 @@ -uniform float timer; vec4 ProcessTexel() { diff --git a/wadsrc/static/shaders/glsl/fuzz_jagged.fp b/wadsrc/static/shaders/glsl/fuzz_jagged.fp index c088c7b30..083c22068 100644 --- a/wadsrc/static/shaders/glsl/fuzz_jagged.fp +++ b/wadsrc/static/shaders/glsl/fuzz_jagged.fp @@ -1,5 +1,4 @@ //created by Evil Space Tomato -uniform float timer; vec4 ProcessTexel() { diff --git a/wadsrc/static/shaders/glsl/fuzz_noise.fp b/wadsrc/static/shaders/glsl/fuzz_noise.fp index 75afc251f..6d1c0094c 100644 --- a/wadsrc/static/shaders/glsl/fuzz_noise.fp +++ b/wadsrc/static/shaders/glsl/fuzz_noise.fp @@ -1,5 +1,4 @@ //created by Evil Space Tomato -uniform float timer; vec4 ProcessTexel() { diff --git a/wadsrc/static/shaders/glsl/fuzz_smooth.fp b/wadsrc/static/shaders/glsl/fuzz_smooth.fp index 4261d5415..3c642c399 100644 --- a/wadsrc/static/shaders/glsl/fuzz_smooth.fp +++ b/wadsrc/static/shaders/glsl/fuzz_smooth.fp @@ -1,5 +1,4 @@ //created by Evil Space Tomato -uniform float timer; vec4 ProcessTexel() { diff --git a/wadsrc/static/shaders/glsl/fuzz_smoothnoise.fp b/wadsrc/static/shaders/glsl/fuzz_smoothnoise.fp index bfe04ec16..d446a3d0a 100644 --- a/wadsrc/static/shaders/glsl/fuzz_smoothnoise.fp +++ b/wadsrc/static/shaders/glsl/fuzz_smoothnoise.fp @@ -1,5 +1,4 @@ //created by Evil Space Tomato -uniform float timer; vec4 ProcessTexel() { diff --git a/wadsrc/static/shaders/glsl/fuzz_smoothtranslucent.fp b/wadsrc/static/shaders/glsl/fuzz_smoothtranslucent.fp index 75bee0330..1b727a1bc 100644 --- a/wadsrc/static/shaders/glsl/fuzz_smoothtranslucent.fp +++ b/wadsrc/static/shaders/glsl/fuzz_smoothtranslucent.fp @@ -1,5 +1,4 @@ //created by Evil Space Tomato -uniform float timer; vec4 ProcessTexel() { diff --git a/wadsrc/static/shaders/glsl/fuzz_software.fp b/wadsrc/static/shaders/glsl/fuzz_software.fp index b17b49b8a..7840ce8be 100644 --- a/wadsrc/static/shaders/glsl/fuzz_software.fp +++ b/wadsrc/static/shaders/glsl/fuzz_software.fp @@ -1,5 +1,4 @@ // Fuzz effect as rendered by the software renderer -uniform float timer; #define FUZZTABLE 50 #define FUZZ_RANDOM_X_SIZE 100 diff --git a/wadsrc/static/shaders/glsl/fuzz_standard.fp b/wadsrc/static/shaders/glsl/fuzz_standard.fp index b467f64b2..3eb3b67e6 100644 --- a/wadsrc/static/shaders/glsl/fuzz_standard.fp +++ b/wadsrc/static/shaders/glsl/fuzz_standard.fp @@ -1,5 +1,4 @@ //created by Evil Space Tomato -uniform float timer; vec4 ProcessTexel() { diff --git a/wadsrc/static/shaders/glsl/fuzz_swirly.fp b/wadsrc/static/shaders/glsl/fuzz_swirly.fp index 86a66ac8e..266858999 100644 --- a/wadsrc/static/shaders/glsl/fuzz_swirly.fp +++ b/wadsrc/static/shaders/glsl/fuzz_swirly.fp @@ -1,5 +1,4 @@ //created by Evil Space Tomato -uniform float timer; vec4 ProcessTexel() { diff --git a/wadsrc/static/shaders/glsl/main.fp b/wadsrc/static/shaders/glsl/main.fp index 25a2c14d2..444a9811f 100644 --- a/wadsrc/static/shaders/glsl/main.fp +++ b/wadsrc/static/shaders/glsl/main.fp @@ -12,26 +12,10 @@ out vec4 FragFog; out vec4 FragNormal; #endif -#ifdef SHADER_STORAGE_LIGHTS - layout(std430, binding = 1) buffer LightBufferSSO - { - vec4 lights[]; - }; -#elif defined NUM_UBO_LIGHTS - /*layout(std140)*/ uniform LightBufferUBO - { - vec4 lights[NUM_UBO_LIGHTS]; - }; -#endif - - -uniform sampler2D tex; -uniform sampler2D ShadowMap; - vec4 Process(vec4 color); vec4 ProcessTexel(); vec4 ProcessLight(vec4 color); - +vec3 ProcessMaterial(vec3 material, vec3 color); //=========================================================================== // @@ -237,47 +221,21 @@ float shadowmapAttenuation(vec4 lightpos, float shadowIndex) #endif } -#endif - -//=========================================================================== -// -// Standard lambertian diffuse light calculation -// -//=========================================================================== - -float diffuseContribution(vec3 lightDirection, vec3 normal) +float shadowAttenuation(vec4 lightpos, float lightcolorA) { - return max(dot(normal, lightDirection), 0.0f); -} - -//=========================================================================== -// -// Calculates the brightness of a dynamic point light -// Todo: Find a better way to define which lighting model to use. -// (Specular mode has been removed for now.) -// -//=========================================================================== - -float pointLightAttenuation(vec4 lightpos, float lightcolorA) -{ - float attenuation = max(lightpos.w - distance(pixelpos.xyz, lightpos.xyz),0.0) / lightpos.w; - if (attenuation == 0.0) return 0.0; -#ifdef SUPPORTS_SHADOWMAPS float shadowIndex = abs(lightcolorA) - 1.0; - attenuation *= shadowmapAttenuation(lightpos, shadowIndex); -#endif - if (lightcolorA >= 0.0) // Sign bit is the attenuated light flag - { - return attenuation; - } - else - { - vec3 lightDirection = normalize(lightpos.xyz - pixelpos.xyz); - float diffuseAmount = diffuseContribution(lightDirection, normalize(vWorldNormal.xyz)); - return attenuation * diffuseAmount; - } + return shadowmapAttenuation(lightpos, shadowIndex); } +#else + +float shadowAttenuation(vec4 lightpos, float lightcolorA) +{ + return 1.0; +} + +#endif + float spotLightAttenuation(vec4 lightpos, vec3 spotdir, float lightCosInnerAngle, float lightCosOuterAngle) { vec3 lightDirection = normalize(lightpos.xyz - pixelpos.xyz); @@ -285,6 +243,62 @@ float spotLightAttenuation(vec4 lightpos, vec3 spotdir, float lightCosInnerAngle return smoothstep(lightCosOuterAngle, lightCosInnerAngle, cosDir); } +//=========================================================================== +// +// Adjust normal vector according to the normal map +// +//=========================================================================== + +#if defined(NORMALMAP) +mat3 cotangent_frame(vec3 n, vec3 p, vec2 uv) +{ + // get edge vectors of the pixel triangle + vec3 dp1 = dFdx(p); + vec3 dp2 = dFdy(p); + vec2 duv1 = dFdx(uv); + vec2 duv2 = dFdy(uv); + + // solve the linear system + vec3 dp2perp = cross(n, dp2); // cross(dp2, n); + vec3 dp1perp = cross(dp1, n); // cross(n, dp1); + vec3 t = dp2perp * duv1.x + dp1perp * duv2.x; + vec3 b = dp2perp * duv1.y + dp1perp * duv2.y; + + // construct a scale-invariant frame + float invmax = inversesqrt(max(dot(t,t), dot(b,b))); + return mat3(t * invmax, b * invmax, n); +} + +vec3 ApplyNormalMap() +{ + #define WITH_NORMALMAP_UNSIGNED + #define WITH_NORMALMAP_GREEN_UP + //#define WITH_NORMALMAP_2CHANNEL + + vec3 interpolatedNormal = normalize(vWorldNormal.xyz); + + vec3 map = texture(normaltexture, vTexCoord.st).xyz; + #if defined(WITH_NORMALMAP_UNSIGNED) + map = map * 255./127. - 128./127.; // Math so "odd" because 0.5 cannot be precisely described in an unsigned format + #endif + #if defined(WITH_NORMALMAP_2CHANNEL) + map.z = sqrt(1 - dot(map.xy, map.xy)); + #endif + #if defined(WITH_NORMALMAP_GREEN_UP) + map.y = -map.y; + #endif + + mat3 tbn = cotangent_frame(interpolatedNormal, pixelpos.xyz, vTexCoord.st); + vec3 bumpedNormal = normalize(tbn * map); + return bumpedNormal; +} +#else +vec3 ApplyNormalMap() +{ + return normalize(vWorldNormal.xyz); +} +#endif + //=========================================================================== // // Calculate light @@ -299,7 +313,7 @@ float spotLightAttenuation(vec4 lightpos, vec3 spotdir, float lightCosInnerAngle // //=========================================================================== -vec4 getLightColor(float fogdist, float fogfactor) +vec4 getLightColor(vec4 material, float fogdist, float fogfactor) { vec4 color = vColor; @@ -341,56 +355,9 @@ vec4 getLightColor(float fogdist, float fogfactor) color = ProcessLight(color); // - // apply dynamic lights (except additive) + // apply dynamic lights // - - vec4 dynlight = uDynLightColor; - -#if defined NUM_UBO_LIGHTS || defined SHADER_STORAGE_LIGHTS - if (uLightIndex >= 0) - { - ivec4 lightRange = ivec4(lights[uLightIndex]) + ivec4(uLightIndex + 1); - if (lightRange.z > lightRange.x) - { - // - // modulated lights - // - for(int i=lightRange.x; i= 0) - { - ivec4 lightRange = ivec4(lights[uLightIndex]) + ivec4(uLightIndex + 1); - if (lightRange.w > lightRange.z) - { - vec4 addlight = vec4(0.0,0.0,0.0,0.0); - - // - // additive lights - these can be done after the alpha test. - // - for(int i=lightRange.z; i 0.0) // Skip shadow map test if possible + { + attenuation *= shadowAttenuation(lightpos, lightcolor.a); + return lightcolor.rgb * attenuation; + } + else + { + return vec3(0.0); + } +} + +vec3 ProcessMaterial(vec3 material, vec3 color) +{ + if (uLightIndex >= 0) + { + vec4 dynlight = uDynLightColor; + vec3 normal = ApplyNormalMap(); + + ivec4 lightRange = ivec4(lights[uLightIndex]) + ivec4(uLightIndex + 1); + + if (lightRange.z > lightRange.x) + { + // modulated lights + for(int i=lightRange.x; i lightRange.z) + { + vec4 addlight = vec4(0.0,0.0,0.0,0.0); + + // additive lights + for(int i=lightRange.z; i= 0) + { + ivec4 lightRange = ivec4(lights[uLightIndex]) + ivec4(uLightIndex + 1); + if (lightRange.z > lightRange.x) + { + // + // modulated lights + // + for(int i=lightRange.x; i 0.0) + { + attenuation *= shadowAttenuation(lightpos, lightcolor.a); + + vec3 radiance = lightcolor.rgb * attenuation; + + // cook-torrance brdf + float NDF = DistributionGGX(N, H, roughness); + float G = GeometrySmith(N, V, L, roughness); + vec3 F = fresnelSchlick(clamp(dot(H, V), 0.0, 1.0), F0); + + vec3 kS = F; + vec3 kD = (vec3(1.0) - kS) * (1.0 - metallic); + + vec3 nominator = NDF * G * F; + float denominator = 4.0 * clamp(dot(N, V), 0.0, 1.0) * clamp(dot(N, L), 0.0, 1.0); + vec3 specular = nominator / max(denominator, 0.001); + + Lo += (kD * albedo / PI + specular) * radiance; + } + } + // + // subtractive lights + // + for(int i=lightRange.y; i 0.0) + { + attenuation *= shadowAttenuation(lightpos, lightcolor.a); + + vec3 radiance = lightcolor.rgb * attenuation; + + // cook-torrance brdf + float NDF = DistributionGGX(N, H, roughness); + float G = GeometrySmith(N, V, L, roughness); + vec3 F = fresnelSchlick(clamp(dot(H, V), 0.0, 1.0), F0); + + vec3 kS = F; + vec3 kD = (vec3(1.0) - kS) * (1.0 - metallic); + + vec3 nominator = NDF * G * F; + float denominator = 4.0 * clamp(dot(N, V), 0.0, 1.0) * clamp(dot(N, L), 0.0, 1.0); + vec3 specular = nominator / max(denominator, 0.001); + + Lo -= (kD * albedo / PI + specular) * radiance; + } + } + } + } + + // Pretend we sampled the sector light level from an irradiance map + + vec3 F = fresnelSchlickRoughness(clamp(dot(N, V), 0.0, 1.0), F0, roughness); + + vec3 kS = F; + vec3 kD = 1.0 - kS; + + vec3 irradiance = ambientLight; // texture(irradianceMap, N).rgb + vec3 diffuse = irradiance * albedo; + + //kD *= 1.0 - metallic; + //const float MAX_REFLECTION_LOD = 4.0; + //vec3 prefilteredColor = textureLod(prefilterMap, R, roughness * MAX_REFLECTION_LOD).rgb; + //vec2 envBRDF = texture(brdfLUT, vec2(clamp(dot(N, V), 0.0, 1.0), roughness)).rg; + //vec3 specular = prefilteredColor * (F * envBRDF.x + envBRDF.y); + + //vec3 ambient = (kD * diffuse + specular) * ao; + vec3 ambient = (kD * diffuse) * ao; + + vec3 color = ambient + Lo; + + // Tonemap (reinhard) and apply sRGB gamma + //color = color / (color + vec3(1.0)); + return pow(color, vec3(1.0 / 2.2)); +} diff --git a/wadsrc/static/shaders/glsl/material_specular.fp b/wadsrc/static/shaders/glsl/material_specular.fp new file mode 100644 index 000000000..ab61d6cf9 --- /dev/null +++ b/wadsrc/static/shaders/glsl/material_specular.fp @@ -0,0 +1,97 @@ + +vec2 lightAttenuation(int i, vec3 normal, vec3 viewdir, float lightcolorA) +{ + vec4 lightpos = lights[i]; + vec4 lightspot1 = lights[i+2]; + vec4 lightspot2 = lights[i+3]; + + float lightdistance = distance(lightpos.xyz, pixelpos.xyz); + if (lightpos.w < lightdistance) + return vec2(0.0); // Early out lights touching surface but not this fragment + + float attenuation = clamp((lightpos.w - lightdistance) / lightpos.w, 0.0, 1.0); + + if (lightspot1.w == 1.0) + attenuation *= spotLightAttenuation(lightpos, lightspot1.xyz, lightspot2.x, lightspot2.y); + + vec3 lightdir = normalize(lightpos.xyz - pixelpos.xyz); + + if (lightcolorA < 0.0) // Sign bit is the attenuated light flag + attenuation *= clamp(dot(normal, lightdir), 0.0, 1.0); + + if (attenuation > 0.0) // Skip shadow map test if possible + attenuation *= shadowAttenuation(lightpos, lightcolorA); + + if (attenuation <= 0.0) + return vec2(0.0); + + float glossiness = uSpecularMaterial.x; + float specularLevel = uSpecularMaterial.y; + + vec3 halfdir = normalize(viewdir + lightdir); + float specAngle = clamp(dot(halfdir, normal), 0.0f, 1.0f); + float phExp = glossiness * 4.0f; + return vec2(attenuation, attenuation * specularLevel * pow(specAngle, phExp)); +} + +vec3 ProcessMaterial(vec3 material, vec3 color) +{ + if (uLightIndex >= 0) + { + vec4 dynlight = uDynLightColor; + vec4 specular = vec4(0.0, 0.0, 0.0, 1.0); + + vec3 normal = ApplyNormalMap(); + vec3 viewdir = normalize(uCameraPos.xyz - pixelpos.xyz); + + ivec4 lightRange = ivec4(lights[uLightIndex]) + ivec4(uLightIndex + 1); + + if (lightRange.z > lightRange.x) + { + // modulated lights + for(int i=lightRange.x; i lightRange.z) + { + vec4 addlight = vec4(0.0,0.0,0.0,0.0); + + // additive lights + for(int i=lightRange.z; i