From 6524e709a3b5fa36feae0961847ff98abc3c7280 Mon Sep 17 00:00:00 2001 From: ManIsCat2 <137772623+ManIsCat2@users.noreply.github.com> Date: Sat, 13 Sep 2025 14:10:38 +0330 Subject: [PATCH] Fresnel Lighting from F3DEX3 (#941) * Fresnel part1 * Fresnel part2 * vmacu --- data/dynos_bin_gfx.cpp | 2 ++ include/PR/gbi_extension.h | 49 +++++++++++++++++++++++++++++++++ include/gfx_symbols.h | 1 + src/game/rendering_graph_node.c | 14 ++++------ src/pc/gfx/gfx_pc.c | 41 +++++++++++++++++++++++++-- 5 files changed, 96 insertions(+), 11 deletions(-) diff --git a/data/dynos_bin_gfx.cpp b/data/dynos_bin_gfx.cpp index 5d22cfe05..a5d97daba 100644 --- a/data/dynos_bin_gfx.cpp +++ b/data/dynos_bin_gfx.cpp @@ -405,6 +405,8 @@ s64 DynOS_Gfx_ParseGfxConstants(const String& _Arg, bool* found) { gfx_constant(G_LIGHT_MAP_EXT); gfx_constant(G_LIGHTING_ENGINE_EXT); gfx_constant(G_PACKED_NORMALS_EXT); + gfx_constant(G_FRESNEL_COLOR_EXT); + gfx_constant(G_FRESNEL_ALPHA_EXT); gfx_constant(G_COL_PRIM); gfx_constant(G_COL_ENV); diff --git a/include/PR/gbi_extension.h b/include/PR/gbi_extension.h index 1ea2d51b7..f87933cc0 100644 --- a/include/PR/gbi_extension.h +++ b/include/PR/gbi_extension.h @@ -7,6 +7,8 @@ #define G_LIGHT_MAP_EXT 0x00000800 #define G_LIGHTING_ENGINE_EXT 0x00004000 #define G_PACKED_NORMALS_EXT 0x00000080 +#define G_FRESNEL_COLOR_EXT 0x00000040 +#define G_FRESNEL_ALPHA_EXT 0x00400000 ////////// // DJUI // @@ -118,3 +120,50 @@ (_SHIFTL(G_PPARTTOCOLOR, 24, 8)) | (_SHIFTL(color, 16, 8)), \ ((2 * ((part) + 1)) + 1 + offset) \ }} + +//////////////////// +//// G_MOVEWORD //// +//////////////////// + +#define G_MW_FX 0x00 /* replaces G_MW_MATRIX which is no longer supported */ +#define G_MWO_FRESNEL 0x0C + +/** + * Fresnel - Feature suggested by thecozies + * Enabled with the G_FRESNEL bit in geometry mode. + * The dot product between a vertex normal and the vector from the vertex to the + * camera is computed. The offset and scale here convert this to a shade alpha + * value. This is useful for making surfaces fade between transparent when + * viewed straight-on and opaque when viewed at a large angle, or for applying a + * fake "outline" around the border of meshes. + * + * If using Fresnel, you need to set the camera world position whenever you set + * the VP matrix, viewport, etc. See SPCameraWorld. + * + * The RSP does: + * s16 dotProduct = dot(vertex normal, camera pos - vertex pos); + * dotProduct = abs(dotProduct); // 0 = points to side, 7FFF = points at or away + * s32 factor = ((scale * dotProduct) >> 15) + offset; + * s16 result = clamp(factor << 8, 0, 7FFF); + * color_or_alpha = result >> 7; + * + * At dotMax, color_or_alpha = FF, result = 7F80, factor = 7F + * At dotMin, color_or_alpha = 00, result = 0, factor = 0 + * 7F = ((scale * dotMax) >> 15) + offset + * 00 = ((scale * dotMin) >> 15) + offset + * Subtract: 7F = (scale * (dotMax - dotMin)) >> 15 + * 3F8000 = scale * (dotMax - dotMin) + * scale = 3F8000 / (dotMax - dotMin) <-- + * offset = -(((3F8000 / (dotMax - dotMin)) * dotMin) >> 15) + * offset = -((7F * dotMin) / (dotMax - dotMin)) <-- + * + * To convert in the opposite direction: + * ((7F - offset) << 15) / scale = dotMax + * ((00 - offset) << 15) / scale = dotMin + */ +#define gSPFresnel(pkt, scale, offset) \ + gMoveWd(pkt, G_MW_FX, G_MWO_FRESNEL, \ + (_SHIFTL((scale), 16, 16) | _SHIFTL((offset), 0, 16))) +#define gsSPFresnel(scale, offset) \ + gsMoveWd(G_MW_FX, G_MWO_FRESNEL, \ + (_SHIFTL((scale), 16, 16) | _SHIFTL((offset), 0, 16))) diff --git a/include/gfx_symbols.h b/include/gfx_symbols.h index 0c8801995..a12858e8d 100644 --- a/include/gfx_symbols.h +++ b/include/gfx_symbols.h @@ -56,6 +56,7 @@ define_gfx_symbol(gsMoveWd, 3, false, GFX_PARAM_TYPE_INT, GFX_PARAM_TYPE_INT, GF define_gfx_symbol(gsSPLoadGeometryMode, 1, false, GFX_PARAM_TYPE_INT); define_gfx_symbol(gsSPVertexNonGlobal, 3, true, GFX_PARAM_TYPE_VTX, GFX_PARAM_TYPE_INT, GFX_PARAM_TYPE_INT); define_gfx_symbol(gsSPCopyPlayerPartToColor, 3, false, GFX_PARAM_TYPE_INT, GFX_PARAM_TYPE_INT, GFX_PARAM_TYPE_INT); +define_gfx_symbol(gsSPFresnel, 2, false, GFX_PARAM_TYPE_INT, GFX_PARAM_TYPE_INT); define_gfx_symbol_manual(gsSPTexture, 5, false, GFX_PARAM_TYPE_INT, GFX_PARAM_TYPE_INT, GFX_PARAM_TYPE_INT, GFX_PARAM_TYPE_INT, GFX_PARAM_TYPE_INT); define_gfx_symbol_manual(gsSPSetGeometryMode, 1, false, GFX_PARAM_TYPE_INT); diff --git a/src/game/rendering_graph_node.c b/src/game/rendering_graph_node.c index 0edbfbf2f..cfebccc00 100644 --- a/src/game/rendering_graph_node.c +++ b/src/game/rendering_graph_node.c @@ -718,14 +718,12 @@ static void geo_process_camera(struct GraphNodeCamera *node) { mtxf_copy(gCamera->mtx, gMatStack[gMatStackIndex]); } - // compute inverse matrix for lighting engine - if (le_is_enabled()) { - Mat4 invCameraMatrix; - if (mtxf_inverse_non_affine(invCameraMatrix, gCamera->mtx)) { - Mtx *invMtx = alloc_display_list(sizeof(Mtx)); - mtxf_to_mtx(invMtx, invCameraMatrix); - gSPMatrix(gDisplayListHead++, invMtx, G_MTX_INVERSE_CAMERA_EXT); - } + // compute inverse matrix for lighting engine and fresnel + Mat4 invCameraMatrix; + if (mtxf_inverse_non_affine(invCameraMatrix, gCamera->mtx)) { + Mtx *invMtx = alloc_display_list(sizeof(Mtx)); + mtxf_to_mtx(invMtx, invCameraMatrix); + gSPMatrix(gDisplayListHead++, invMtx, G_MTX_INVERSE_CAMERA_EXT); } if (node->fnNode.node.children != 0) { diff --git a/src/pc/gfx/gfx_pc.c b/src/pc/gfx/gfx_pc.c index 21329aca6..03b9f1558 100644 --- a/src/pc/gfx/gfx_pc.c +++ b/src/pc/gfx/gfx_pc.c @@ -61,6 +61,7 @@ static struct RSP { uint32_t geometry_mode; int16_t fog_mul, fog_offset; + int16_t fresnel_scale, fresnel_offset; struct { // U0.16 @@ -850,6 +851,33 @@ static void OPTIMIZE_O3 gfx_sp_vertex(size_t n_vertices, size_t dest_index, cons d->color.b *= vtxB; } + if (rsp.geometry_mode & (G_FRESNEL_COLOR_EXT | G_FRESNEL_ALPHA_EXT)) { + Vec3f vpos = { v->ob[0], v->ob[1], v->ob[2] }; + Vec3f vnormal = { nx / 255.0f, ny / 255.0f, nz / 255.0f }; + // transform vpos and vnormal to world space + gfx_local_to_world_space(vpos, vnormal); + + Vec3f viewDir = { + sInverseCameraMatrix[3][0] - vpos[0], + sInverseCameraMatrix[3][1] - vpos[1], + sInverseCameraMatrix[3][2] - vpos[2] + }; + vec3f_normalize(viewDir); + vec3f_normalize(vnormal); + + int32_t dot = (int32_t) (fabsf(vec3f_dot(vnormal, viewDir)) * 32767.0f); + int32_t factor = ((rsp.fresnel_scale * dot) >> 15) + rsp.fresnel_offset; + int32_t fresnel = clamp(factor << 8, 0, 0x7FFF); + uint8_t result = (uint8_t) (fresnel >> 7); + + if (rsp.geometry_mode & G_FRESNEL_COLOR_EXT) { + d->color.r = d->color.g = d->color.b = result; + } + if (rsp.geometry_mode & G_FRESNEL_ALPHA_EXT) { + d->color.a = result; + } + } + if (rsp.geometry_mode & G_TEXTURE_GEN) { float dotx = 0, doty = 0; dotx += nx * rsp.current_lookat_coeffs[0][0]; @@ -970,8 +998,9 @@ static void OPTIMIZE_O3 gfx_sp_vertex(size_t n_vertices, size_t dest_index, cons if (fog_z > 255) fog_z = 255; d->fog_z = fog_z; } - - d->color.a = v->cn[3]; + if (!(rsp.geometry_mode & G_FRESNEL_ALPHA_EXT)) { + d->color.a = v->cn[3]; + } } } @@ -1297,7 +1326,7 @@ static void gfx_sp_copymem(uint8_t idx, uint16_t dstofs, uint16_t srcofs, UNUSED } #endif -static void gfx_sp_moveword(uint8_t index, UNUSED uint16_t offset, uint32_t data) { +static void gfx_sp_moveword(uint8_t index, uint16_t offset, uint32_t data) { switch (index) { case G_MW_NUMLIGHT: #ifdef F3DEX_GBI_2 @@ -1318,6 +1347,12 @@ static void gfx_sp_moveword(uint8_t index, UNUSED uint16_t offset, uint32_t data sDepthZMult = (gProjectionVanillaFarValue - gProjectionMaxNearValue) / (gProjectionVanillaFarValue - gProjectionVanillaNearValue); sDepthZSub = gProjectionVanillaNearValue; + break; + case G_MW_FX: + if (offset == G_MWO_FRESNEL) { + rsp.fresnel_scale = (int16_t)(data >> 16); + rsp.fresnel_offset = (int16_t)data; + } break; } }