From 886a76515dacb010f9cbf731316ec03f58caaea7 Mon Sep 17 00:00:00 2001 From: "Mr.doob" Date: Thu, 30 Oct 2025 16:56:49 +0900 Subject: [PATCH 1/7] WebGLRenderer: Add energy conservation to sheen layer. --- .../lights_physical_pars_fragment.glsl.js | 44 +++++++++++++++++-- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/src/renderers/shaders/ShaderChunk/lights_physical_pars_fragment.glsl.js b/src/renderers/shaders/ShaderChunk/lights_physical_pars_fragment.glsl.js index 330e502c8fe783..8cf05dc09a2794 100644 --- a/src/renderers/shaders/ShaderChunk/lights_physical_pars_fragment.glsl.js +++ b/src/renderers/shaders/ShaderChunk/lights_physical_pars_fragment.glsl.js @@ -530,6 +530,22 @@ void RE_Direct_Physical( const in IncidentLight directLight, const in vec3 geome sheenSpecularDirect += irradiance * BRDF_Sheen( directLight.direction, geometryViewDir, geometryNormal, material.sheenColor, material.sheenRoughness ); + // Energy conservation: reduce base layers by sheen energy + float dotNV = saturate( dot( geometryNormal, geometryViewDir ) ); + float maxSheenColor = max( max( material.sheenColor.r, material.sheenColor.g ), material.sheenColor.b ); + + // Approximate directional albedo + float x = 1.0 - dotNV; + float r = material.sheenRoughness; + float r2 = r * r; + float baseAlbedo = 0.04 + 0.96 * r2; + float fresnelFactor = pow( x, 5.0 * ( 1.0 - r ) ); + float E_sheen = baseAlbedo * mix( 1.0, fresnelFactor, 1.0 - r2 * r ); + + // Scale base layers + float sheenScaling = 1.0 - maxSheenColor * E_sheen; + irradiance *= sheenScaling; + #endif reflectedLight.directSpecular += irradiance * BRDF_GGX_Multiscatter( directLight.direction, geometryViewDir, geometryNormal, material ); @@ -555,6 +571,20 @@ void RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 irradia sheenSpecularIndirect += irradiance * material.sheenColor * IBLSheenBRDF( geometryNormal, geometryViewDir, material.sheenRoughness ); + // Energy conservation for indirect lighting + float dotNV = saturate( dot( geometryNormal, geometryViewDir ) ); + float maxSheenColor = max( max( material.sheenColor.r, material.sheenColor.g ), material.sheenColor.b ); + + // Approximate directional albedo (same as direct lighting) + float x = 1.0 - dotNV; + float r = material.sheenRoughness; + float r2 = r * r; + float baseAlbedo = 0.04 + 0.96 * r2; + float fresnelFactor = pow( x, 5.0 * ( 1.0 - r ) ); + float E_sheen = baseAlbedo * mix( 1.0, fresnelFactor, 1.0 - r2 * r ); + + float sheenScaling = 1.0 - maxSheenColor * E_sheen; + #endif // Both indirect specular and indirect diffuse light accumulate here @@ -576,10 +606,16 @@ void RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 irradia vec3 totalScattering = singleScattering + multiScattering; vec3 diffuse = material.diffuseColor * ( 1.0 - max( max( totalScattering.r, totalScattering.g ), totalScattering.b ) ); - reflectedLight.indirectSpecular += radiance * singleScattering; - reflectedLight.indirectSpecular += multiScattering * cosineWeightedIrradiance; - - reflectedLight.indirectDiffuse += diffuse * cosineWeightedIrradiance; + #ifdef USE_SHEEN + // Apply energy conservation scaling to base layers + reflectedLight.indirectSpecular += radiance * singleScattering * sheenScaling; + reflectedLight.indirectSpecular += multiScattering * cosineWeightedIrradiance * sheenScaling; + reflectedLight.indirectDiffuse += diffuse * cosineWeightedIrradiance * sheenScaling; + #else + reflectedLight.indirectSpecular += radiance * singleScattering; + reflectedLight.indirectSpecular += multiScattering * cosineWeightedIrradiance; + reflectedLight.indirectDiffuse += diffuse * cosineWeightedIrradiance; + #endif } From 4cf8b6223f86cbb3652b218104c934ece3810d34 Mon Sep 17 00:00:00 2001 From: "Mr.doob" Date: Thu, 30 Oct 2025 17:22:38 +0900 Subject: [PATCH 2/7] Updated screenshot. --- .../screenshots/webgl_loader_gltf_sheen.jpg | Bin 25693 -> 26624 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/examples/screenshots/webgl_loader_gltf_sheen.jpg b/examples/screenshots/webgl_loader_gltf_sheen.jpg index 97bd0d7600040e9dd83f690b9c1310c2763bde5f..228ccd1e048ac2409ec57092b6f1daaa59ccc691 100644 GIT binary patch delta 24115 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 Qg5r4o@0yXC_V3*P0N7ZhUH||9 From 939b39a75dfd186fa841e02d59a30fbb24ddc7f3 Mon Sep 17 00:00:00 2001 From: "Mr.doob" Date: Thu, 30 Oct 2025 18:11:55 +0900 Subject: [PATCH 3/7] WebGLRenderer: Implement Sheen LUT. --- .../screenshots/webgl_loader_gltf_sheen.jpg | Bin 26624 -> 26679 bytes src/renderers/WebGLRenderer.js | 8 + .../lights_physical_pars_fragment.glsl.js | 33 +- src/renderers/shaders/SheenLUTData.js | 65 ++++ src/renderers/shaders/UniformsLib.js | 4 +- utils/generateSheenLUT.js | 305 ++++++++++++++++++ 6 files changed, 395 insertions(+), 20 deletions(-) create mode 100644 src/renderers/shaders/SheenLUTData.js create mode 100644 utils/generateSheenLUT.js diff --git a/examples/screenshots/webgl_loader_gltf_sheen.jpg b/examples/screenshots/webgl_loader_gltf_sheen.jpg index 228ccd1e048ac2409ec57092b6f1daaa59ccc691..cca3ae3398bec5304234b03692f680b240948e1e 100644 GIT binary patch delta 23945 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 TlraZ>@-+Xyb8X7&|7QOOp6WR@ delta 23859 zcmYgXXHe5$u#F;OK@g-@6_DORI@kaK2_2*a6r_X@dT&4ffFKw^2>}gFfe`6Zl0azE zr3;}*Lhrrz=5yY>5AV}{xpQZBXYZcfJ?9dBUwZrd(vSC7fMWT1OxaB@KR6Z7BXnT4 z;1YK-db-6a$eqsSb8R!#yUff5KGr8uIGs}e;^%cH`p}9Ods5X}u*8_kpYva$WzZ9W zHY|b+w>S+Za&%!@=t*8+PQuU<1G8meEi9d`YKNjwpckR1!74~cpXu&fy$rx zu0LTd>7O$M>}~!w^DY{Q+1?oogp*#` z>oNF?Nd_%crH36t!DL*i`U5o++x(K4>ZcKk zB<73NCN87c`ibVdJ%zpo7m#Uze(VxS)cUBf>CpW?Zu`N1K78DT-1TMH7xN87MwMts z`>?k?4l%Q~w^|3|#Uxp_dLI;Ji9x4#*9vetx+C)l;hFgd>%~cRIOVc%?!d^m7?Gn5 z>RB;nHtkHmvQX)a>18ceJ+ztVtJl}s)+L+!dMx(y>Mj_hanw>jdfa03!~4WuHs%2UkMg_~bC+%_cK zCB&`BGXfCk{n_tBf1L5&-y3m&%> z3u-tjSH%s^vwsg!Hbh}9dB?Jz@m`VP0Thc7vk9V@cio=sPOs0uB)|ECHf(vlGP`8b zY+t5$2ES4JCwOn!q*OPW^OkLft4aFmaK4Ui3J(OZ&6_@{$EgP zke1!eV`vP;xp1Ug8$Xy1hdWz@jW%+`PqiDhZwAhes4o1-^vdi7#*Q;zt2`WeD0%x3 zPaMP(gSeThtlzmzB4fN?u9SAAyh5!PG>G4F+t^&pn{}wDk8<>bm@L?R&Umt3U+1J@ zdljaDb{4hx>=3XVS32pDFnNEzIaFRn@RK74+>LOpXnkJ$?Bw=;p*wHU_}pts_L35h z+EwSChbhlyE##y*0XwN8-O_|$pw5C6_7@bCeL;+z4(Bo&;p?>8?b-?mTR+Vl9ymFu zhJPEpPfdahv5M{{62_91W|em7G;F_)Jo9<6!_h9+)>x-DW_w>@)oDl*3$aLA+fKdV ziL=(i(mOIU9tG`;F%|7yxN?S`hj9G`y&v|DInR%?hwGd5}5Z=zYe@}_`E z`sJ3SB0Xx`+S_Mu{Mh-HWQ1$wt!sL%;0^l#$@*XIjVGCA<83;H6?O>wi^C_&%5uE* zf%gM6SKn$q>H{rOK86LYnKXe#HjMRbivhcwC2atgzJ4tiiBGTNUCdquz}Vk*RG~1* zI5Ac2+&)2csOB#ybN5bY zGqVM&1W)U<@YIUM_Z8EeR0kK+1kS6N2a{#ZgFk1~qO8xyl?tSA%7zG_D=W|L(tNsb zyJC<;227 zk-h)U{QnhiCE(H#=U))D>u+aLxa66{w(C_rh|kXfm+sbZ?V7EFx_pTKZhFL|(?}5t z>Cwq-moWxN2T~&E`?lEXExO`5zUipBtxS6bmW05F*!S7!b@#;+%RR21S(E6a7$F~9 z(xny;_O389+IES?rt9sn5Vx!M@-_=YyjSOe3T$jW(8`^&9%p89`nx2;J`}fuGWzY& zb-U>4hU&GUFHc5x5`6Sh83YSfN=gXO$?{do9Dp&&D`O9iA8@}3fiEt@aRzX=#QreB z7WJ@N$J$)kXV<$#yRPy1kL6F|Ui$W4d@=fD%RkGKMNGzU+BxHJlpgW3#Q4%E&$MBE ztpGgGsSmGD#ix#*73qf+biq0A=jd>N^n#+QsllGri^z}RTPnefk9Jp@#|M(}#aSSBC1LTzee#M+>j{$PrXeY)UxAD-kTT3ZOp@nKX|l znQ5G}DPbX{>W9|8YyP?jk1Ml%13H4)qyoU&wFFN-RJu9IY7Hg!z6(BlHAY$SY8G+g z<7*5={*m$clq{otkX1?jwC)R|nB!LP56^HCoXgRF6DAO5+)wC&`%iu=w&BG!561{( z&s3|8E0Mqt`>RBA)2wzTPGl)}w=b*Swh6)v=wnh8=o(+r-1_KMt!;M0`x4o}Y908V zow1hH6`nOfBtZJo0=0XDEU`SrawV079DFJTelYaGk|4Sev|8b!BpHDSk-Gb--Dd~+ z^H(pm!Fa8Mdwu?4i0$G2es$E3-GzDAjq}QV$XW5^*K(L4(~OzJr%H)%ceUqyKU6;| zF2;3wf`zNj@WJEZtX0#|=#(BmBmje{9B$6%oqjQab#SUGjW-%Un!8mEZ>N4FMmIUl z-pGfTn#u|MUR-U1uCS9BS*8>!=e+YaN-NhhB^cNE2UqTyZ-%&}7mnnmP zw`K=j4qDRP1AV-zZX9e>dLM?`ImNFY7T0l?)b>E99Uf4O4fHw!8sO`TKq>n7jOyc= zE|m;E!!l@X%@^^KX}b5_g$r8SmyN1A-=O$u_ognxv^j+cP%EQU+k^wFwTW@FjR3=+ zX7OfNTfVtdd1Rfw5mKpMBQm z{KMTKMUzw|sMrps`z@?8?rUz_F!UE><~~7{dyIymCi>9pq8ged59gnkT#V*f; z`3m@)On;JRf{8}R@2$0JdPP9|Ojtrb` z=BSAtfCn8@TeBF%A(rNg`oA~Lz@F4~6BtIsmiqdXD&5gkbm?+K_PD!Ed)!vXvh}6Z z5q!<`w)}KBS=B`MWE0NWuw_S@rC~)K1tplfN59UP<{v5zHD{$VA78&{CHC*v%&X6p zt|8BcZh(Y9UoXE!R$Z0_&hr0)A}Wu#R{)&hU zuoGKRHcz~BS3|ZN@%YB`Gv_u$1Le)J`I~P&Vsy$S9Km)e3F#3do_S=&)|2!O`ap-zJT~sqUGa#72j(C>g7;F4zkvk{qaVMA% zwT9vlUJXPtN;^9q$URQ-KpooD8RL=~zBrwz4sHf>{2fRCNLcRp_@2qxgkD^Nv51Sn?mqGdfFvhc>62h zm_T*eTRI6um6Z};V68Smv^^d7zyd0MD$Y^KLRfy%fR1$mfmj^S?{?5&uOm=SNP`-=f zlNu{oqkIGM_`qTeZa#htIxZ4#xiaYC=ojQMKcsXHPJT04>3){Kj~vqO@Et0xUhWKZ zcsA^t!P8;@^}5r+O!;0{Q#L4Bxx}~-I9)a*;rr3|pdtG9r*Zk16TaSZKvgMfcYU`F zURBir-6SEVb=~{Hrn6=upQ13~&j)Q*9vTnNqFl_4e0&h8_k9;~u6J~b=$c2()8yFn z%DAqN*3rNCeP(;WGQ+LwT~_pD++8&qltnwGqiMCiCL6(}w!tvh{{?|E~j~dfORDO+rA_*D6|i z-lY><`U~vfUr=tTs)GkO@FMO+N*yfJ2Ze3EVRjn2U^S5G%0@{NC&>!oDIw!oO}qv# zWNkPb|HLONV*eDYOL`y| z>`C6K)z<|wxP%7UbZdxkWnCki?bx%%Q1)%50l$Y4^OiYe{NzgQ((G~XLST9IUB)kJ zR6Jf)yi*(Z8+V^((whmY*!Ehvr7dUsPip^*O-PjqPnqJ&LKW}Fni$O5_$0&wc3d2w zVNuTw3;t4Oo@t)Y-G1rE&C1TQN}~w=_d+h?9rpmU4_Bt7I*b9eq&QT{36`K;9z1DK zKVqTn?;Ml6aO@C-AKN5^jg#;M{Q050s@W{;Ird>v4e_@5W6ua-nV;!9Uum6WINkMb zKVC*SGpme3fUFMJl!ZVsra{wnw)M_gQ7lZLCOjAhudJR4zgt@tLJ5fpvSe%Pyb`DT z4$x}kPwiB?6ft%69zIF>o$f8oR{sjp$;f8#fm)nrDxa1ANPRtexvta)Lm1hci=IBi z@w)t1oGDW{-*L08>=%au-JPHbn^%QY@j3O37__f-1Z7-fM~5UUc`yMkc}V zrevtg+9uH=emo0|UM5w~h|x}4xr<{TQYLhF^$RB_i13Y#EN23{B;p(EotqHNCOPkX zyraCXM>@A^jqa;5xs}^Uv{=vNp3hYsQ;!< zZR%=MpPG9J770t08In7{6MqkF#KR9t>_;>7UXipnhWLaoA$0ojUk1)FALnM6I*u-nY_!VxsuQlFr8uq*QSyr|H z&d(xZLsgXypTWh;0XSFP6VZcBm>Ryu8lGP(q{RxC_uGZA9F9T{?W!d*W^aCG`DMUd zE1Ua4_I}2X!mMkvJ~@cC2u8fqrFL*YY_|o;pAz~r5#QRX z$BG7lp>(Su`J!F7j6#Xw$cBB7DPC;5xFmg`ZbI$A>F%BUHk(m@ znK}*wOWl{@?xFmr7l*e*%(yq!C2F!ZfVZ`j~y;G+f@ zS`4!YKp~y2dG1>Az&bRq1L95?yofK>8LF#Xh?@^-%`1gxxzE$H22%7Jx;T>^pG)0E zefnH>{GpIPDE7xsL=|5iz46kth~4upi+W1fh!xPt%wgN1SR?MY4s$6QpX?gqQ7AcD zT`LpSlbTyej9IVAEepeDi|pfaS`kq=`UPPGb}IE9TYtbnB~ z;Fd1q!zY`Iy8)XuGBK^I24xMI{a0SZ?95MJeQeyqlY6pw6m$3E!;6;hbIB&~l@db3 zGxSB}nvW{{8dg}JKO-}L^mrw9?Lr> z&s89syF2>ukqW(U3sxiT87zOEzd`|2zgjXV+=NRgfu}+sE)fMYB{m9rU&wPq1XD`m zm=~YBvA-Fna5_-kZm0C~x9z?Qwj>ooT+Jie*=9-5n;iWGL48w7y}}YJo?AY%ec|X6 z@u%@P=IaDV!NqBWqFt86e9g-Suxhr;>D$UxWN>4Hv zAA5eVn5fIel+KtOEZSzEw>KmJ^CO&TB-*vUz~A|vqu42pwArfYS1176ntuR-YjCHM zWY$*l`Z^}%6H7(GiUN}V;jsUwz5pLTIm8u1iX}OxOSWPB<&3s}FLTIcRc0Mq38ww> zIE1xFU1?#6rQaEF1PiQAc|zx&9c7#=AN1vXR;5^W#c>eV8JBrwcsJB9Z}boO zJXhN<$Oi^LzdkK_Nu4eFb-KMv->nbb%LC1Z&4ZEetX|qEoA@~!7ui(jsOr0Ga~MYn zf|-~wfLo5iS>K6e;CRHgib6+~4}a-XcH~y~wqm-LLkv1UrfuQ#_omc}rk=-I7}{Ic z!1N4YAME?$*nGB-&w+!5ClI@etoCUngR=?{F7+4GXb8M9U_QFoX`h{J4wHe~{sle% zJv|WWmksp~2q1PJpnb~$r@-jWb})`uH+azl^E$X?bYCl_v#qUj-Qi)GZ4dm&BhH2w z(P|G2tJctqneuRC(Du=5iI~kS5jbA{X$%>1ShEot!7b`$-0+$A9nv;9g)LRgJ_M|r z<(z+3U&tu34Rwn+$k4h!sD!>c-j+s`DLGp0yFN)_F)qDs@97SfDz&>~i%i9%u9=;& zRUh@t`~}^zQh&c&>-Dqg9&4(Yw@2(}dZ`ywZ=*hz1wkV-e!jApx;>3x5h*1Y1t8rG zzCc4D{KWe5hG!{@Ct9_-nXELtFb)8$dvo+uNf=L9NWWmlN{YQguXgY0{a1f9J7dWm zTU8BJ6nGoDL3QP$;+a&~kE^0X*rv~#SbpOn>t_T?O&B^DTD@CU%2FN9QK%V$4UwPE z-o!%ut7BMOczFXOrmUf4^M+y@MW+OA^|pf@|BmA?_PE&bUt=;9u0)D)y8J>ROLiv?X84EbYwwblL&R$Hvy zO!pw!6J;haSJ2ClR}8NPHipwn78gew24thl$tKv?zVbMel=MQGW`jHYdksG@R(?!& zyF@EdE8TWSk5ek3QOj@X>r23Slj$duRGiw=7WJD0stqzuY(Du%nSJiV?v_orB6}1O z8|p*6^s>|sW|QqXk#XMX&?g6n;bSvN{`EBy_`zc_?SaI1v$`liu;mZi6NJ_LH!cX= zF*wocWpH|rlBMGKxZ>H1)xE4Efvhtoz{2RDFFM#F=F@wS>t7Jl1&>Zt7`9t_sa|lH z`|{#~R=g868(RmNKF6RL(KA|Gzz}R%t94yP{h&oj)XsDOX4d6fy8F2S<3Zbs(|*4# zFPG$+X_qdK)m#TWVx zPMzPOtVKDD*I(m)wNqE@WND>FAE&_%6*)BacNvEow)92*6@%cg_RiUG)F`Vc!Z~AR zeQi$3=wsA~m;NTF~U=3eI)12K7n1zh>{{=N!o&s8dRL=>jU4KE>4x>Z$XLN2S zcQE8(G~T5=;+><;yl-;&ndZ=Jd0>me{1+rCt$4!&MlxGfu`WOJUD;Na7y4p* z^|S2*WUH+PvEqDo@%$c5MCCtclao|u?ppo^0jjO2o%NOeDcZQKZO8bQWt`REG}FfT z%#S^x09@{Tw$Au?Lwx)B-P&hu%X$ncQ+z!kuRT$&1WxNuwc&znxbaSgs1nMDxN^IW znxmjSNs4s~FZD*{Df=1qvLph+zI%YXk$(F}%|9GgBVlX+FpO*$dteEmr7Y>Y! zGcRlnD$-cvz=+)$E#{#^T_uF5_xTp@#YA4PoXH#oM`(b~l(q!_`>pl*`afd$S|HFY zbWg?SHiXrn{IV?0`!m_UMEi2x3w67iN78kyM>2j zEEJ69d7&3{mnpoT?YHBW@fl^z1+Hmu^?1VI#_Erq2&C6#b%0)dTd>LQovjQJw#j-X zdlWWPxF}+~+mcDUwNULlf9ZzRtO?0I&;IMj-0GkFv{bhKd_tXGe%v}Tj2BggmEy6Nw1OIT{Y-kG@9Cv&E1hN7i|CpH=Bke7|Wy~ke3NaMPn*N99|1#KcOGxC#>mlAEgb^1eub2AmBthGtm!lrO6if#20SNoXGAlijWhpV zH2K=Mpd9}=c_9STRA>yN@6rJ}f=j$VL8QJ9jN!a7 zY1LOzq<2uV$$)k!^q_MFptj&0R9f(OVVzr8&tK40;A9Ezd@6V5GmHtEOEo#qmcJ#X zBU}3=M*GNlYh{}mGX8lz&9>T!4dg+?U)EmJEW$gwVT5o9E|E+T4wcro?GR_eFNKaU zm=>zK9^gbwQ!5t0x${aP1E}#i%1|r*WF!1wq(AfjiA;DK8`d9VN~0R4FaWQ?BgAD5 zXfe?8`BR6n{3N&bOi~XYwJ2wOZeQhPKJ#luW@d?tN@TQ0wIPNH! zM*SD$-OK4V$P{{N2M?{(sEs>kJ2FJRzWfZhFx8lbY4j#>3uy7JBF+{R;2;ZmQFlBRc!C_ZZI!V4T9XZa*0k)pr z688NI`cq+1Lq!8Qnx9=aJMsR#6n?W`_VA8|L_!Y#ufVn~P=bbYv{`vo!$PsKT@)Yi z7lc2~w7adcz_^2h+i|kC)_C&<*3y0z3n#G7#Ske`hXI~XyLr=_>aWoJQCP;F#nfG z31`7W7u;u(+arj*v>xZ5!Sg^~@iaga7-{o+u4+)7H<07vsH*_#FY`z1?$Ez`>w_e+ zcS^D|_&?dO@FKk|ZqKp3fLPe6b}a1-Vnt26e|Vv0uaFVkj>39h0%XdJJsM!H_`7KV>r2n{{`he^UypDVkxZ77qjS+Lq5CPZ6*2Y;|1 zIw;QUpN4>`Kk8NIT9hup*IWN_OfPm3i*yXKHM&YRlV;5SGBbk*%cKtdD6ql`s0x^c zNlqS4@SN~$rCcXvF>XI}$gqg7%j3NK|97vQQhV~mDTJSn= zPtFqFvuEmSb$00dvWETZ@yN*t{3YRA1LZ&1XAH6;8)r4pPCO#p2kKz6DuO(=22V3O zUuM1RRv*o7X;)Z35@4YLX*{Btn$V}dp(+^JD$nis`Sq^YfN{!S5XNjIxnYh=9JQ z`v8;r$FYznQt>u|w`S7KFv@?p+(gVqW|Ep0e1-F^H9Nr4D>M5Ii-D!t3RlfCbd$@* zH2;g58xtDn_LO(f)90fvax^RGRT$bG%+M~*CHmgJ7`)*5_)gh0Ns(V!TYca;H#1)M z?IKNz5-352EqE|IDOB@HJ+3)O4I=L=%?6AqyHhYqGexBXo4C#Vnc)+TDQBQyTOFpU z5LgRly>8oicvyWe3Qp0>Et-@ssQ@D9#n_=rma4Y1ntOe{V>!Duz&WJ_p8sR zUTxhITHXO?8d0(4s4_WL%cInf#=mr9rlU@es{Wo1Xgz1 zP^7uO_f&ZKMp}hA5t@fY6dx+m-#F9iPIi1Z)q8gy4Q6ba5}D%TCqeTaQaDtLMX@1r zGo-^DVA1a4#__3gsM|RvTrE^;+n`YrxI)hz=+F#Yr;99bJ(%Fz%0%FaXY~@>;%8_t z{Pu&I97wl`L1xpGjWwmmRdXbD@%a5xR>rnq+}I^VvF3DDT)Y{Ws~kp!=Ew9sT$_)G zOTuN$`i_|FaYZ&}-I1y+ICpsGWjd~-q^ZY=dN?zEr6fhI3cpdzpXD1K`xgW>iq$d{ zoza{pp7Cz|1uN)fGRF&)NwOafIjm8U1pn|?HA70(miD=t- zh_4Cz(fjFFp_uW1dzLhDwmimN-}|$e<_BE`mB7pnaXFpyYIpoRw=F6@Ou~azLN|h@ zS!3;Ni_JWvVaomyh~;yua{qXMV%u4}S4Z;4ots^3Z_~FrQ|88uCZT<3W>g4sR9;*?|BRGMDxuDqs zE5|%s!xL^j)gBjwv+V!5r5<8zy51-9Cpb`9Sl)M-kKKa)iP6QLt0ET23Gi3lH9gN) zXMh%1iJMpqJu-hKw@zdD1&#?@=QgP8tpC6x>Etegw?xi!^@uK5F5GZc@DJ@02}&jtUFMM0d-wO>}mli&d*P z{Q{V?^&vIF>l&~yj`b~7&iwm6yWHkiLmeXcPOq(aWo7+`?3eGg7iv&i!tl=PsTX%_R-@jgF7Hl;+VR z{(-W%ZI@osVq^G?fznd@_B6V>8I6i+r(ZAE*WpDo5tcXMGn*D9Q^ecZNSn`nxfs{q zhMhVZmSfBH1zTN4Jf8CY@y}y-ReD?Hd)-XQcO{kK2>#ed6K+%qlgDhI@hHT3()Ihg{*cFd2&mF67;|GQh>L<5=D-BeXye$T} zS=TnweOWOKA`rf8fA0UGA7Y19n`zw^v4o~P07m7tY8v8ZTx?J~#I@=W14KP<2?Erb=Ei!JoVixnsp{lO@OeTt|1J^>;@T9z z(F&zGAlr~7a;8yI|IOe|bfi)9)OJ#^ZD{g6Zq6D5iUKn%EM_^|edWkLfHSql?Y)%6 z+KFl!SjExrQLE`5|7Gn7Ll}*jZLdPOcUZu(2bz?>)XaWuRF!x>{=!rub8?SvfOxi7 zzktj9FNlE=XnHpP_6BCJ%HCME*A@;KZF!|{=^XSp3s1UW7HiIcsepZtJCB#e>h=8) zUdBjLC3KoEEcij{m_~~_@3#05D&3sihP)SNUY)qEz{)`R5!hn#bke@ zsANr69OqAWn>ehj)WfW8`Z*B}!Z?mAO|JC+hnIT3b051$)jy66Vl5m z-!d)z(#@7Jck3wq!Tki$TefNQ`PvLmyb7Nb2{><(^4WUD-DWD)O_YCm1|`sNMzov! zJA`PmmkLD4YxaQaIT^sFh5bqWo$|uEUP(loa#wN|6l&3xzl;vgeoI|nj;{gv+Nm>r z{64pJ^R|*I9kv*-%|DY)tNja#krCT4GR%Y}J06{U)cH;-jB6Swv`RPe7=LF8W(a`h zqTwo8GX$154J(Uxw&@<_t!O{!tq{X!=*Z0*r_i&SzY|GOh*thHe|=@rNNY;rCaX15p<$K@D5DLve+TR_4uTQPvqdb%I7poI) z-umLjqnyi1c!?ey@+dGZ*qDv!s;g)+%uTwpHs9HcY5UwUeG5-1%M zj})RuDYAMXQ-rX!qwCS|kndBmS+dQt;vrCzs-Wjh-h6n_biGM{(8B#N^x-SGiB|pF zX9ng`WpyvpIK7(}e(i(M#&N`2VqQ}4QdXGpuGLvThQlP%yZDnmOG;9+y-QDrwV60n zNm2k)=k{+vjtXh#TPPc;RVmtYd%1!?JwPI4Tn|pH3h8p^By(`0*f6+9~GRr2I_G7%VAl2$aB?C%dfV+_&@Cb>99m97I%1}Sdd)CAu6~wmT#_S z#x%xQpH|4mitc;2&;4l(>i@|`26!7|aJ38MRjd74yy&^lJ$@nspATN^Y23SoN_eZa z#dvA!XaH>BWL@=vV~U>czN0?{``&isi=cD&DydMA*G~ZI<1#RxDEra_3*~iTWgx?p z69#k}#n<8)cOJP}QOGZz2wy7}XwE25sHk|}myL|@sxP$=mGID?)bz{70f!b(lD7lX zLUg7)^EEX^OdXg5wsnVG&230fA9=RoY6ng&lCvfywo=DI)#N~eNBTq$v z>M3VIBwbF;VX8&BUGzcOkRElyM5c(0foXl|V@yf^Vor!Zp{tCMeB>gPcB;IhI(wnE zuaY$O#?xJ(YC2KO|KmdhaOro~_u=;y^Ag%sB9J=Gwsrk;cJ?_{U(m3CD@x&8i$^I%HFh;=xG7R%ME z;FFVT@4&nj!m%en9A3dWh8?^^x^^|lwo+;B^xwBF8TiC#_zY?m$iyF_g;KQ5Wy zEUcQP9JslAi6BLP#B1GN)xs%ixx0dm=LsTK-n;H gQ(afrOkYu?(^mMeqWQtW8v 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%0Dm=q@Lc}@hpnW1UHzLr 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 diff --git a/src/renderers/WebGLRenderer.js b/src/renderers/WebGLRenderer.js index 95dac4f95731df..83de85a32597c6 100644 --- a/src/renderers/WebGLRenderer.js +++ b/src/renderers/WebGLRenderer.js @@ -55,6 +55,7 @@ import { WebGLUniformsGroups } from './webgl/WebGLUniformsGroups.js'; import { createCanvasElement, probeAsync, warnOnce, error, warn, log } from '../utils.js'; import { ColorManagement } from '../math/ColorManagement.js'; import { getDFGLUT } from './shaders/DFGLUTData.js'; +import { getSheenLUT } from './shaders/SheenLUTData.js'; /** * This renderer uses WebGL 2 to display scenes. @@ -2520,6 +2521,13 @@ class WebGLRenderer { } + // Set Sheen LUT for sheen materials + if ( material.sheen > 0 ) { + + m_uniforms.sheenLUT.value = getSheenLUT(); + + } + if ( refreshMaterial ) { p_uniforms.setValue( _gl, 'toneMappingExposure', _this.toneMappingExposure ); diff --git a/src/renderers/shaders/ShaderChunk/lights_physical_pars_fragment.glsl.js b/src/renderers/shaders/ShaderChunk/lights_physical_pars_fragment.glsl.js index 8cf05dc09a2794..a3ea3c85c0d22a 100644 --- a/src/renderers/shaders/ShaderChunk/lights_physical_pars_fragment.glsl.js +++ b/src/renderers/shaders/ShaderChunk/lights_physical_pars_fragment.glsl.js @@ -1,6 +1,7 @@ export default /* glsl */` uniform sampler2D dfgLUT; +uniform sampler2D sheenLUT; struct PhysicalMaterial { @@ -530,20 +531,19 @@ void RE_Direct_Physical( const in IncidentLight directLight, const in vec3 geome sheenSpecularDirect += irradiance * BRDF_Sheen( directLight.direction, geometryViewDir, geometryNormal, material.sheenColor, material.sheenRoughness ); - // Energy conservation: reduce base layers by sheen energy float dotNV = saturate( dot( geometryNormal, geometryViewDir ) ); float maxSheenColor = max( max( material.sheenColor.r, material.sheenColor.g ), material.sheenColor.b ); - - // Approximate directional albedo - float x = 1.0 - dotNV; - float r = material.sheenRoughness; - float r2 = r * r; - float baseAlbedo = 0.04 + 0.96 * r2; - float fresnelFactor = pow( x, 5.0 * ( 1.0 - r ) ); - float E_sheen = baseAlbedo * mix( 1.0, fresnelFactor, 1.0 - r2 * r ); - - // Scale base layers - float sheenScaling = 1.0 - maxSheenColor * E_sheen; + + // Lookup directional albedo for both view and light angles + float E_sheen_V = texture2D( sheenLUT, vec2( dotNV, material.sheenRoughness ) ).r; + float E_sheen_L = texture2D( sheenLUT, vec2( dotNL, material.sheenRoughness ) ).r; + + // Take minimum for proper energy conservation + float sheenScaling = min( + 1.0 - maxSheenColor * E_sheen_V, + 1.0 - maxSheenColor * E_sheen_L + ); + irradiance *= sheenScaling; #endif @@ -575,13 +575,8 @@ void RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 irradia float dotNV = saturate( dot( geometryNormal, geometryViewDir ) ); float maxSheenColor = max( max( material.sheenColor.r, material.sheenColor.g ), material.sheenColor.b ); - // Approximate directional albedo (same as direct lighting) - float x = 1.0 - dotNV; - float r = material.sheenRoughness; - float r2 = r * r; - float baseAlbedo = 0.04 + 0.96 * r2; - float fresnelFactor = pow( x, 5.0 * ( 1.0 - r ) ); - float E_sheen = baseAlbedo * mix( 1.0, fresnelFactor, 1.0 - r2 * r ); + // Lookup directional albedo from LUT + float E_sheen = texture2D( sheenLUT, vec2( dotNV, material.sheenRoughness ) ).r; float sheenScaling = 1.0 - maxSheenColor * E_sheen; diff --git a/src/renderers/shaders/SheenLUTData.js b/src/renderers/shaders/SheenLUTData.js new file mode 100644 index 00000000000000..08f6e10b03a0b7 --- /dev/null +++ b/src/renderers/shaders/SheenLUTData.js @@ -0,0 +1,65 @@ +/** + * Precomputed Sheen LUT for Charlie Sheen BRDF + * Resolution: 32x32 + * Samples: 4096 per texel + * Format: R16F (directional albedo for energy conservation) + * Based on Charlie sheen BRDF model for cloth-like materials + */ + +import { DataTexture } from '../../textures/DataTexture.js'; +import { RedFormat, HalfFloatType, LinearFilter, ClampToEdgeWrapping } from '../../constants.js'; + +const DATA = new Uint16Array( [ + 0x40ca, 0x3e9c, 0x3cb8, 0x3ad1, 0x38ef, 0x371f, 0x351d, 0x3348, 0x3123, 0x2f2b, 0x2cef, 0x2ab2, 0x2878, 0x25dd, 0x2389, 0x20bd, 0x1dd0, 0x1af0, 0x1804, 0x147e, 0x10d5, 0x0cf8, 0x08d9, 0x0474, 0x01e6, 0x00be, 0x0042, 0x0014, 0x0005, 0x0000, 0x0000, 0x0000, + 0x3e4f, 0x3d01, 0x3c14, 0x3ab9, 0x3993, 0x38a2, 0x37b5, 0x3667, 0x3550, 0x3464, 0x333d, 0x31f0, 0x30d9, 0x2fde, 0x2e57, 0x2d13, 0x2c06, 0x2a53, 0x28eb, 0x278d, 0x25b8, 0x2443, 0x223b, 0x2075, 0x1e37, 0x1c32, 0x1971, 0x16b5, 0x13b4, 0x0ffc, 0x0afe, 0x0440, + 0x3d2c, 0x3c4a, 0x3b49, 0x3a42, 0x3967, 0x38af, 0x3810, 0x370e, 0x361f, 0x354e, 0x3496, 0x33eb, 0x32d0, 0x31d7, 0x30fd, 0x303d, 0x2f2b, 0x2e06, 0x2d08, 0x2c2a, 0x2ad7, 0x298f, 0x2877, 0x2713, 0x2583, 0x2435, 0x2244, 0x2085, 0x1e40, 0x1c10, 0x18cd, 0x14bb, + 0x3c8c, 0x3bba, 0x3ab6, 0x39e4, 0x3933, 0x389a, 0x3815, 0x3740, 0x3670, 0x35b7, 0x3511, 0x347c, 0x33ec, 0x32fc, 0x3223, 0x3160, 0x30b0, 0x3012, 0x2f0a, 0x2e0c, 0x2d2a, 0x2c60, 0x2b59, 0x2a1b, 0x2903, 0x280e, 0x2671, 0x2500, 0x2388, 0x2170, 0x1f60, 0x1c7f, + 0x3c24, 0x3b26, 0x3a4c, 0x399b, 0x3904, 0x3881, 0x380d, 0x374c, 0x3693, 0x35ed, 0x3555, 0x34cc, 0x344f, 0x33b9, 0x32e9, 0x322a, 0x317b, 0x30dc, 0x304a, 0x2f8a, 0x2e97, 0x2db9, 0x2cf0, 0x2c3a, 0x2b29, 0x29ff, 0x28f3, 0x2804, 0x265d, 0x24e3, 0x232f, 0x20ed, + 0x3bb4, 0x3ab9, 0x39fc, 0x3960, 0x38dc, 0x3868, 0x3801, 0x3749, 0x36a2, 0x360a, 0x357f, 0x34ff, 0x348a, 0x341e, 0x3375, 0x32bd, 0x3213, 0x3175, 0x30e4, 0x305d, 0x2fc0, 0x2eda, 0x2e05, 0x2d41, 0x2c8c, 0x2bcc, 0x2a9c, 0x2985, 0x2887, 0x2740, 0x259e, 0x2425, + 0x3b45, 0x3a65, 0x39bc, 0x3931, 0x38ba, 0x3851, 0x37e7, 0x373f, 0x36a6, 0x3619, 0x3599, 0x3522, 0x34b4, 0x344d, 0x33dd, 0x332c, 0x3287, 0x31ee, 0x315e, 0x30d8, 0x305b, 0x2fcc, 0x2ef2, 0x2e26, 0x2d68, 0x2cb7, 0x2c13, 0x2af5, 0x29d9, 0x28d3, 0x27c3, 0x2605, + 0x3aec, 0x3a22, 0x3989, 0x390a, 0x389d, 0x383d, 0x37ce, 0x3732, 0x36a4, 0x3622, 0x35a9, 0x353a, 0x34d2, 0x3471, 0x3416, 0x3383, 0x32e3, 0x324e, 0x31c2, 0x313e, 0x30c1, 0x304c, 0x2fbd, 0x2eee, 0x2e2b, 0x2d74, 0x2cc7, 0x2c26, 0x2b1c, 0x2a00, 0x28f6, 0x27fa, + 0x3aa5, 0x39eb, 0x395e, 0x38e9, 0x3884, 0x382b, 0x37b6, 0x3725, 0x36a0, 0x3626, 0x35b4, 0x354b, 0x34e8, 0x348c, 0x3435, 0x33c7, 0x332e, 0x329d, 0x3214, 0x3193, 0x3118, 0x30a4, 0x3036, 0x2f9d, 0x2ed8, 0x2e1d, 0x2d6c, 0x2cc4, 0x2c26, 0x2b20, 0x2a04, 0x28f8, + 0x3a6a, 0x39bd, 0x3939, 0x38cc, 0x386e, 0x381b, 0x37a0, 0x3717, 0x369a, 0x3627, 0x35bb, 0x3557, 0x34f9, 0x34a1, 0x344e, 0x33ff, 0x336b, 0x32df, 0x3259, 0x31db, 0x3163, 0x30f0, 0x3083, 0x301b, 0x2f71, 0x2eb5, 0x2e02, 0x2d57, 0x2cb4, 0x2c18, 0x2b09, 0x29ef, + 0x3a38, 0x3995, 0x391a, 0x38b3, 0x385b, 0x380c, 0x378b, 0x370a, 0x3694, 0x3626, 0x35c0, 0x3560, 0x3507, 0x34b2, 0x3462, 0x3417, 0x339e, 0x3316, 0x3294, 0x3219, 0x31a3, 0x3132, 0x30c7, 0x3060, 0x2ffb, 0x2f3e, 0x2e8a, 0x2ddd, 0x2d37, 0x2c99, 0x2c00, 0x2add, + 0x3a0d, 0x3973, 0x38ff, 0x389e, 0x384a, 0x37fe, 0x3778, 0x36fe, 0x368d, 0x3624, 0x35c3, 0x3567, 0x3511, 0x34c0, 0x3473, 0x342a, 0x33ca, 0x3345, 0x32c7, 0x324f, 0x31db, 0x316d, 0x3103, 0x309d, 0x303b, 0x2fbb, 0x2f06, 0x2e59, 0x2db2, 0x2d11, 0x2c76, 0x2bc2, + 0x39e8, 0x3956, 0x38e7, 0x388a, 0x383a, 0x37e7, 0x3767, 0x36f2, 0x3686, 0x3622, 0x35c4, 0x356d, 0x351a, 0x34cc, 0x3481, 0x343b, 0x33ef, 0x336e, 0x32f4, 0x327e, 0x320d, 0x31a1, 0x3138, 0x30d4, 0x3073, 0x3016, 0x2f79, 0x2ecb, 0x2e24, 0x2d82, 0x2ce5, 0x2c4e, + 0x39c8, 0x393c, 0x38d2, 0x3879, 0x382d, 0x37d2, 0x3757, 0x36e7, 0x367f, 0x361f, 0x35c5, 0x3571, 0x3521, 0x34d5, 0x348d, 0x3449, 0x3408, 0x3392, 0x331b, 0x32a8, 0x3239, 0x31cf, 0x3168, 0x3105, 0x30a6, 0x304a, 0x2fe1, 0x2f35, 0x2e8d, 0x2deb, 0x2d4e, 0x2cb6, + 0x39ab, 0x3925, 0x38bf, 0x386a, 0x3820, 0x37be, 0x3748, 0x36dc, 0x3679, 0x361c, 0x35c5, 0x3574, 0x3526, 0x34dd, 0x3498, 0x3455, 0x3416, 0x33b2, 0x333d, 0x32cd, 0x3261, 0x31f8, 0x3194, 0x3132, 0x30d4, 0x3079, 0x3021, 0x2f96, 0x2ef0, 0x2e4e, 0x2db1, 0x2d18, + 0x3991, 0x3910, 0x38ae, 0x385c, 0x3815, 0x37ad, 0x373b, 0x36d3, 0x3672, 0x3619, 0x35c5, 0x3576, 0x352b, 0x34e4, 0x34a0, 0x3460, 0x3422, 0x33ce, 0x335c, 0x32ee, 0x3284, 0x321e, 0x31bb, 0x315b, 0x30ff, 0x30a5, 0x304d, 0x2ff1, 0x2f4b, 0x2eaa, 0x2e0d, 0x2d74, + 0x397a, 0x38fd, 0x389e, 0x384f, 0x380b, 0x379c, 0x372e, 0x36ca, 0x366c, 0x3616, 0x35c4, 0x3577, 0x352f, 0x34ea, 0x34a8, 0x3469, 0x342d, 0x33e7, 0x3377, 0x330c, 0x32a4, 0x3240, 0x31df, 0x3181, 0x3126, 0x30cd, 0x3077, 0x3022, 0x2fa1, 0x2f01, 0x2e65, 0x2dcc, + 0x3966, 0x38ec, 0x3890, 0x3844, 0x3802, 0x378d, 0x3723, 0x36c1, 0x3667, 0x3612, 0x35c3, 0x3579, 0x3532, 0x34ef, 0x34af, 0x3472, 0x3437, 0x33fd, 0x3390, 0x3327, 0x32c2, 0x325f, 0x3200, 0x31a4, 0x314a, 0x30f2, 0x309d, 0x304a, 0x2ff1, 0x2f52, 0x2eb7, 0x2e1f, + 0x3953, 0x38dd, 0x3884, 0x3839, 0x37f2, 0x377f, 0x3718, 0x36b9, 0x3661, 0x360f, 0x35c2, 0x357a, 0x3535, 0x34f3, 0x34b5, 0x3479, 0x3440, 0x3409, 0x33a7, 0x3340, 0x32dc, 0x327c, 0x321e, 0x31c3, 0x316b, 0x3115, 0x30c0, 0x306e, 0x301e, 0x2f9e, 0x2f05, 0x2e6e, + 0x3942, 0x38cf, 0x3878, 0x3830, 0x37e2, 0x3773, 0x370e, 0x36b1, 0x365c, 0x360c, 0x35c1, 0x357a, 0x3537, 0x34f7, 0x34ba, 0x3480, 0x3448, 0x3412, 0x33bb, 0x3356, 0x32f5, 0x3296, 0x323a, 0x31e1, 0x318a, 0x3135, 0x30e2, 0x3090, 0x3041, 0x2fe6, 0x2f4e, 0x2eb8, + 0x3932, 0x38c2, 0x386d, 0x3827, 0x37d3, 0x3767, 0x3704, 0x36aa, 0x3657, 0x3609, 0x35c0, 0x357b, 0x3539, 0x34fb, 0x34bf, 0x3486, 0x344f, 0x341a, 0x33ce, 0x336b, 0x330b, 0x32ae, 0x3254, 0x31fc, 0x31a6, 0x3152, 0x3101, 0x30b0, 0x3062, 0x3015, 0x2f93, 0x2efe, + 0x3924, 0x38b6, 0x3863, 0x381e, 0x37c6, 0x375b, 0x36fb, 0x36a3, 0x3652, 0x3606, 0x35be, 0x357b, 0x353b, 0x34fe, 0x34c3, 0x348b, 0x3456, 0x3422, 0x33df, 0x337e, 0x3320, 0x32c5, 0x326c, 0x3215, 0x31c1, 0x316e, 0x311e, 0x30cf, 0x3081, 0x3035, 0x2fd4, 0x2f41, + 0x3916, 0x38ab, 0x385a, 0x3817, 0x37b9, 0x3751, 0x36f3, 0x369d, 0x364d, 0x3603, 0x35bd, 0x357b, 0x353c, 0x3500, 0x34c7, 0x3490, 0x345c, 0x3429, 0x33ef, 0x3390, 0x3334, 0x32da, 0x3282, 0x322d, 0x31da, 0x3189, 0x3139, 0x30eb, 0x309e, 0x3053, 0x3009, 0x2f80, + 0x390a, 0x38a1, 0x3852, 0x3810, 0x37ad, 0x3747, 0x36eb, 0x3697, 0x3649, 0x3600, 0x35bc, 0x357b, 0x353d, 0x3503, 0x34cb, 0x3495, 0x3461, 0x342f, 0x33fe, 0x33a0, 0x3345, 0x32ed, 0x3297, 0x3243, 0x31f1, 0x31a1, 0x3153, 0x3105, 0x30ba, 0x306f, 0x3026, 0x2fbc, + 0x38ff, 0x3898, 0x384a, 0x3809, 0x37a2, 0x373e, 0x36e4, 0x3691, 0x3645, 0x35fd, 0x35ba, 0x357b, 0x353e, 0x3505, 0x34ce, 0x3499, 0x3466, 0x3435, 0x3406, 0x33b0, 0x3356, 0x32ff, 0x32ab, 0x3258, 0x3207, 0x31b8, 0x316b, 0x311f, 0x30d4, 0x308a, 0x3042, 0x2ff5, + 0x38f4, 0x388f, 0x3842, 0x3803, 0x3797, 0x3736, 0x36dd, 0x368c, 0x3641, 0x35fb, 0x35b9, 0x357a, 0x353f, 0x3507, 0x34d1, 0x349d, 0x346b, 0x343b, 0x340c, 0x33be, 0x3366, 0x3310, 0x32bd, 0x326b, 0x321c, 0x31ce, 0x3181, 0x3136, 0x30ed, 0x30a4, 0x305c, 0x3016, + 0x38ea, 0x3887, 0x383b, 0x37fa, 0x378e, 0x372e, 0x36d7, 0x3687, 0x363d, 0x35f8, 0x35b7, 0x357a, 0x3540, 0x3509, 0x34d3, 0x34a0, 0x346f, 0x3440, 0x3412, 0x33cb, 0x3374, 0x3320, 0x32ce, 0x327e, 0x322f, 0x31e2, 0x3197, 0x314d, 0x3104, 0x30bc, 0x3075, 0x3030, + 0x38e1, 0x387f, 0x3835, 0x37ef, 0x3784, 0x3726, 0x36d1, 0x3682, 0x363a, 0x35f6, 0x35b6, 0x357a, 0x3541, 0x350a, 0x34d6, 0x34a4, 0x3473, 0x3445, 0x3418, 0x33d7, 0x3382, 0x332f, 0x32de, 0x328f, 0x3242, 0x31f6, 0x31ab, 0x3162, 0x311a, 0x30d3, 0x308d, 0x3048, + 0x38d8, 0x3878, 0x382f, 0x37e4, 0x377c, 0x371f, 0x36cb, 0x367e, 0x3636, 0x35f4, 0x35b5, 0x357a, 0x3541, 0x350c, 0x34d8, 0x34a7, 0x3477, 0x3449, 0x341d, 0x33e3, 0x338f, 0x333d, 0x32ed, 0x329f, 0x3253, 0x3208, 0x31bf, 0x3176, 0x312f, 0x30e9, 0x30a4, 0x3060, + 0x38d0, 0x3871, 0x3829, 0x37db, 0x3774, 0x3718, 0x36c5, 0x3679, 0x3633, 0x35f1, 0x35b4, 0x3579, 0x3542, 0x350d, 0x34da, 0x34aa, 0x347b, 0x344d, 0x3422, 0x33ee, 0x339b, 0x334a, 0x32fc, 0x32af, 0x3263, 0x321a, 0x31d1, 0x318a, 0x3143, 0x30fe, 0x30ba, 0x3076, + 0x38c8, 0x386a, 0x3823, 0x37d1, 0x376c, 0x3712, 0x36c0, 0x3675, 0x3630, 0x35ef, 0x35b2, 0x3579, 0x3542, 0x350e, 0x34dc, 0x34ac, 0x347e, 0x3451, 0x3426, 0x33f8, 0x33a7, 0x3357, 0x3309, 0x32bd, 0x3273, 0x322a, 0x31e2, 0x319c, 0x3156, 0x3112, 0x30ce, 0x308b, + 0x38c1, 0x3864, 0x381e, 0x37c9, 0x3764, 0x370b, 0x36bb, 0x3671, 0x362d, 0x35ed, 0x35b1, 0x3578, 0x3543, 0x350f, 0x34de, 0x34af, 0x3481, 0x3455, 0x342a, 0x3401, 0x33b1, 0x3363, 0x3316, 0x32cb, 0x3282, 0x323a, 0x31f3, 0x31ad, 0x3169, 0x3125, 0x30e2, 0x30a0 +] ); + +let lut = null; + +export function getSheenLUT() { + + if ( lut === null ) { + + lut = new DataTexture( DATA, 32, 32, RedFormat, HalfFloatType ); + lut.minFilter = LinearFilter; + lut.magFilter = LinearFilter; + lut.wrapS = ClampToEdgeWrapping; + lut.wrapT = ClampToEdgeWrapping; + lut.generateMipmaps = false; + lut.needsUpdate = true; + + } + + return lut; + +} diff --git a/src/renderers/shaders/UniformsLib.js b/src/renderers/shaders/UniformsLib.js index 55e046b5e79b54..01fd5cdaafff4c 100644 --- a/src/renderers/shaders/UniformsLib.js +++ b/src/renderers/shaders/UniformsLib.js @@ -16,7 +16,9 @@ const UniformsLib = { alphaMap: { value: null }, alphaMapTransform: { value: /*@__PURE__*/ new Matrix3() }, - alphaTest: { value: 0 } + alphaTest: { value: 0 }, + + sheenLUT: { value: null } }, diff --git a/utils/generateSheenLUT.js b/utils/generateSheenLUT.js new file mode 100644 index 00000000000000..695551dbfca842 --- /dev/null +++ b/utils/generateSheenLUT.js @@ -0,0 +1,305 @@ +/** + * Sheen LUT Generator for Three.js + * Generates precomputed lookup table for Charlie Sheen BRDF + * + * Usage: node generateSheenLUT.js [resolution] [samples] + * Example: node generateSheenLUT.js 32 4096 + */ + +import * as fs from 'fs'; + +// Configuration +const RESOLUTION = parseInt(process.argv[2]) || 32; +const SAMPLES_PER_TEXEL = parseInt(process.argv[3]) || 4096; + +console.log(`Generating Sheen LUT: ${RESOLUTION}x${RESOLUTION}, ${SAMPLES_PER_TEXEL} samples per texel`); + +// ============================================================================ +// Math utilities +// ============================================================================ + +function dot(a, b) { + return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; +} + +function normalize(v) { + const len = Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); + return [v[0] / len, v[1] / len, v[2] / len]; +} + +function add(a, b) { + return [a[0] + b[0], a[1] + b[1], a[2] + b[2]]; +} + +function scale(v, s) { + return [v[0] * s, v[1] * s, v[2] * s]; +} + +function clamp(x, min, max) { + return Math.max(min, Math.min(max, x)); +} + +// ============================================================================ +// Random number generation +// ============================================================================ + +function radicalInverse_VdC(bits) { + bits = (bits << 16) | (bits >>> 16); + bits = ((bits & 0x55555555) << 1) | ((bits & 0xAAAAAAAA) >>> 1); + bits = ((bits & 0x33333333) << 2) | ((bits & 0xCCCCCCCC) >>> 2); + bits = ((bits & 0x0F0F0F0F) << 4) | ((bits & 0xF0F0F0F0) >>> 4); + bits = ((bits & 0x00FF00FF) << 8) | ((bits & 0xFF00FF00) >>> 8); + return (bits >>> 0) * 2.3283064365386963e-10; // / 0x100000000 +} + +function hammersley(i, N) { + return [i / N, radicalInverse_VdC(i)]; +} + +// ============================================================================ +// Sampling functions +// ============================================================================ + +// Cosine-weighted hemisphere sampling +function importanceSampleCosine(u, v) { + const phi = 2.0 * Math.PI * u; + const cosTheta = Math.sqrt(1.0 - v); + const sinTheta = Math.sqrt(v); + + return [ + Math.cos(phi) * sinTheta, + Math.sin(phi) * sinTheta, + cosTheta + ]; +} + +// Transform sample from tangent space to world space +function tangentToWorld(sample, N) { + const up = Math.abs(N[2]) < 0.999 ? [0, 0, 1] : [1, 0, 0]; + const tangent = normalize([ + up[1] * N[2] - up[2] * N[1], + up[2] * N[0] - up[0] * N[2], + up[0] * N[1] - up[1] * N[0] + ]); + const bitangent = [ + N[1] * tangent[2] - N[2] * tangent[1], + N[2] * tangent[0] - N[0] * tangent[2], + N[0] * tangent[1] - N[1] * tangent[0] + ]; + + return normalize([ + tangent[0] * sample[0] + bitangent[0] * sample[1] + N[0] * sample[2], + tangent[1] * sample[0] + bitangent[1] * sample[1] + N[1] * sample[2], + tangent[2] * sample[0] + bitangent[2] * sample[1] + N[2] * sample[2] + ]); +} + +// ============================================================================ +// Charlie Sheen BRDF +// ============================================================================ + +function D_Charlie(roughness, NoH) { + // Charlie sheen distribution + const invAlpha = 1.0 / roughness; + const cos2h = NoH * NoH; + const sin2h = Math.max(1.0 - cos2h, 0.0078125); // Avoid division by zero + return (2.0 + invAlpha) * Math.pow(sin2h, invAlpha * 0.5) / (2.0 * Math.PI); +} + +function V_Ashikhmin(NoL, NoV) { + // Ashikhmin visibility term for sheen + return 1.0 / (4.0 * (NoL + NoV - NoL * NoV)); +} + +function charlieSheen(roughness, NoH, NoL, NoV) { + const D = D_Charlie(roughness, NoH); + const V = V_Ashikhmin(NoL, NoV); + return D * V; +} + +// ============================================================================ +// LUT Generation +// ============================================================================ + +function integrateBRDF(roughness, NoV, samples) { + const V = [Math.sqrt(1.0 - NoV * NoV), 0.0, NoV]; + const N = [0.0, 0.0, 1.0]; + + let directionalAlbedo = 0.0; + + for (let i = 0; i < samples; i++) { + const [u, v] = hammersley(i, samples); + const L = importanceSampleCosine(u, v); + const H = normalize(add(V, L)); + + const NoL = Math.max(L[2], 0.0); + const NoH = Math.max(H[2], 0.0); + const VoH = Math.max(dot(V, H), 0.0); + + if (NoL > 0.0) { + const brdf = charlieSheen(roughness, NoH, NoL, NoV); + + // Integrate directional albedo: E(μ) = ∫ f(μ, μ_o) μ_o dω_o + // This gives us the total energy reflected, used for energy conservation + const pdf = NoL / Math.PI; // Cosine-weighted hemisphere PDF + directionalAlbedo += (brdf * NoL) / pdf; + } + } + + directionalAlbedo /= samples; + + return directionalAlbedo; +} + +function generateLUT() { + const data = new Float32Array(RESOLUTION * RESOLUTION); + + for (let y = 0; y < RESOLUTION; y++) { + const roughness = (y + 0.5) / RESOLUTION; + + for (let x = 0; x < RESOLUTION; x++) { + const NoV = (x + 0.5) / RESOLUTION; + + const directionalAlbedo = integrateBRDF(roughness, NoV, SAMPLES_PER_TEXEL); + + const idx = y * RESOLUTION + x; + data[idx] = directionalAlbedo; + } + + // Progress indicator + if ((y + 1) % 4 === 0 || y === RESOLUTION - 1) { + const progress = ((y + 1) / RESOLUTION * 100).toFixed(1); + process.stdout.write(`\rProgress: ${progress}%`); + } + } + + console.log('\nLUT generation complete!'); + return data; +} + +// ============================================================================ +// Half-float conversion (IEEE 754 16-bit) +// ============================================================================ + +function floatToHalf(float) { + const floatView = new Float32Array(1); + const int32View = new Int32Array(floatView.buffer); + + floatView[0] = float; + const x = int32View[0]; + + let bits = (x >> 16) & 0x8000; // Sign bit + let m = (x >> 12) & 0x07ff; // Mantissa + const e = (x >> 23) & 0xff; // Exponent + + // Handle special cases + if (e < 103) { + return bits; + } + + if (e > 142) { + bits |= 0x7c00; + bits |= ((e == 255) ? 0 : 1) && (x & 0x007fffff); + return bits; + } + + if (e < 113) { + m |= 0x0800; + bits |= (m >> (114 - e)) + ((m >> (113 - e)) & 1); + return bits; + } + + bits |= ((e - 112) << 10) | (m >> 1); + bits += m & 1; + return bits; +} + +function convertToHalfFloat(data) { + const halfData = new Uint16Array(data.length); + for (let i = 0; i < data.length; i++) { + halfData[i] = floatToHalf(data[i]); + } + return halfData; +} + +// ============================================================================ +// Output generation +// ============================================================================ + +function formatHexArray(data, itemsPerLine = 32) { + const lines = []; + for (let i = 0; i < data.length; i += itemsPerLine) { + const chunk = Array.from(data.slice(i, i + itemsPerLine)) + .map(x => '0x' + x.toString(16).padStart(4, '0')) + .join(', '); + lines.push('\t' + chunk + (i + itemsPerLine < data.length ? ',' : '')); + } + return lines.join('\n'); +} + +function generateJavaScriptFile(halfData) { + const header = `/** + * Precomputed Sheen LUT for Charlie Sheen BRDF + * Resolution: ${RESOLUTION}x${RESOLUTION} + * Samples: ${SAMPLES_PER_TEXEL} per texel + * Format: R16F (directional albedo for energy conservation) + * Based on Charlie sheen BRDF model for cloth-like materials + */ + +import { DataTexture } from '../../textures/DataTexture.js'; +import { RedFormat, HalfFloatType, LinearFilter, ClampToEdgeWrapping } from '../../constants.js'; + +const DATA = new Uint16Array( [ +`; + + const footer = ` +] ); + +let lut = null; + +export function getSheenLUT() { + +\tif ( lut === null ) { + +\t\tlut = new DataTexture( DATA, ${RESOLUTION}, ${RESOLUTION}, RedFormat, HalfFloatType ); +\t\tlut.minFilter = LinearFilter; +\t\tlut.magFilter = LinearFilter; +\t\tlut.wrapS = ClampToEdgeWrapping; +\t\tlut.wrapT = ClampToEdgeWrapping; +\t\tlut.generateMipmaps = false; +\t\tlut.needsUpdate = true; + +\t} + +\treturn lut; + +} +`; + + const dataStr = formatHexArray(halfData); + return header + dataStr + footer; +} + +// ============================================================================ +// Main +// ============================================================================ + +console.log('Starting LUT generation...\n'); +const startTime = Date.now(); + +const floatData = generateLUT(); +const halfData = convertToHalfFloat(floatData); + +const jsContent = generateJavaScriptFile(halfData); +const outputPath = './SheenLUTData.js'; + +fs.writeFileSync(outputPath, jsContent); + +const elapsed = ((Date.now() - startTime) / 1000).toFixed(2); +console.log(`\nFile written to: ${outputPath}`); +console.log(`Total time: ${elapsed}s`); +console.log(`\nStats:`); +console.log(` Resolution: ${RESOLUTION}x${RESOLUTION}`); +console.log(` Samples per texel: ${SAMPLES_PER_TEXEL}`); +console.log(` Total samples: ${RESOLUTION * RESOLUTION * SAMPLES_PER_TEXEL}`); +console.log(` Data size: ${halfData.length * 2} bytes`); \ No newline at end of file From 9afc9ffe7502d3b90c13fb63fb5a55149e484cef Mon Sep 17 00:00:00 2001 From: mrdoob Date: Thu, 30 Oct 2025 19:02:52 +0900 Subject: [PATCH 4/7] Potential fix for code scanning alert no. 3718: Unused variable, import, function or class Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- utils/generateSheenLUT.js | 1 - 1 file changed, 1 deletion(-) diff --git a/utils/generateSheenLUT.js b/utils/generateSheenLUT.js index 695551dbfca842..511cfb719203de 100644 --- a/utils/generateSheenLUT.js +++ b/utils/generateSheenLUT.js @@ -134,7 +134,6 @@ function integrateBRDF(roughness, NoV, samples) { const NoL = Math.max(L[2], 0.0); const NoH = Math.max(H[2], 0.0); - const VoH = Math.max(dot(V, H), 0.0); if (NoL > 0.0) { const brdf = charlieSheen(roughness, NoH, NoL, NoV); From 2d6f73a4543f0eeec2677a9700c370d3ddffc01f Mon Sep 17 00:00:00 2001 From: mrdoob Date: Thu, 30 Oct 2025 20:52:27 +0900 Subject: [PATCH 5/7] Potential fix for code scanning alert no. 3714: Unused variable, import, function or class Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- utils/generateSheenLUT.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/utils/generateSheenLUT.js b/utils/generateSheenLUT.js index 511cfb719203de..6dd5e6b7258fe0 100644 --- a/utils/generateSheenLUT.js +++ b/utils/generateSheenLUT.js @@ -31,9 +31,7 @@ function add(a, b) { return [a[0] + b[0], a[1] + b[1], a[2] + b[2]]; } -function scale(v, s) { - return [v[0] * s, v[1] * s, v[2] * s]; -} + function clamp(x, min, max) { return Math.max(min, Math.min(max, x)); From 8b8fb6b3c85952ee1e98fbc2838658b4ae0b792e Mon Sep 17 00:00:00 2001 From: mrdoob Date: Fri, 31 Oct 2025 11:01:22 +0900 Subject: [PATCH 6/7] Potential fix for code scanning alert no. 3719: Unused variable, import, function or class Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- utils/generateSheenLUT.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/utils/generateSheenLUT.js b/utils/generateSheenLUT.js index 6dd5e6b7258fe0..27f47a8e4b8ddd 100644 --- a/utils/generateSheenLUT.js +++ b/utils/generateSheenLUT.js @@ -18,9 +18,6 @@ console.log(`Generating Sheen LUT: ${RESOLUTION}x${RESOLUTION}, ${SAMPLES_PER_TE // Math utilities // ============================================================================ -function dot(a, b) { - return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; -} function normalize(v) { const len = Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); From 189eb054ac712d1da8bc3da72f115a6f55453fc8 Mon Sep 17 00:00:00 2001 From: mrdoob Date: Fri, 7 Nov 2025 00:30:13 +0900 Subject: [PATCH 7/7] Potential fix for code scanning alert no. 3716: Unused variable, import, function or class Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- utils/generateSheenLUT.js | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/utils/generateSheenLUT.js b/utils/generateSheenLUT.js index 27f47a8e4b8ddd..bfe485017fde66 100644 --- a/utils/generateSheenLUT.js +++ b/utils/generateSheenLUT.js @@ -69,25 +69,6 @@ function importanceSampleCosine(u, v) { } // Transform sample from tangent space to world space -function tangentToWorld(sample, N) { - const up = Math.abs(N[2]) < 0.999 ? [0, 0, 1] : [1, 0, 0]; - const tangent = normalize([ - up[1] * N[2] - up[2] * N[1], - up[2] * N[0] - up[0] * N[2], - up[0] * N[1] - up[1] * N[0] - ]); - const bitangent = [ - N[1] * tangent[2] - N[2] * tangent[1], - N[2] * tangent[0] - N[0] * tangent[2], - N[0] * tangent[1] - N[1] * tangent[0] - ]; - - return normalize([ - tangent[0] * sample[0] + bitangent[0] * sample[1] + N[0] * sample[2], - tangent[1] * sample[0] + bitangent[1] * sample[1] + N[1] * sample[2], - tangent[2] * sample[0] + bitangent[2] * sample[1] + N[2] * sample[2] - ]); -} // ============================================================================ // Charlie Sheen BRDF