Translucent liquids and glass can refract what is behind them: instead of blending over the scene, the surface shows
a copy of it, shifted. Liquids shift it along the slope of the waves that warp their texture, glass along its normal
map. Both work in the world and in brush entities (func_door, func_wall, ...).
Refraction is opt-in per map. Set _qs_waterrefraction on worldspawn to a strength:
{
"classname" "worldspawn"
"_wateralpha" "0.5"
"_qs_waterrefraction" "1"
}
1is the default strength,0.5half of it,2twice as much.0, a negative value, anything that is not a number or no key at all turns it off.- Only a liquid drawn translucent refracts: the map has to make it so with
_wateralpha,_slimealpha,_lavaalphaor_telealpha, the material with itsalpha(see below), or a brush entity withalphabelow 1. On a Quake 2 map the surface flagsSURF_TRANS33andSURF_TRANS66set that alpha to 0.33 and 0.66. An opaque liquid shows nothing behind it and is drawn as it always was. - A material can ask for refraction without the map: with the flag
MF_REFRACT(strength 1) or its ownrefractionstrength. Either one replaces the map's strength for that liquid, so the flag alone means strength 1 even on a map that sets"_qs_waterrefraction" "2"."refraction": 0keeps one liquid from refracting on a map that asks for it. The worldspawn alpha keys still win over a liquid's materialalpha.
"*slime0": {
"flags": ["MF_TURBULENT"],
"alpha": 0.7,
"refraction": 0.6
}- It works seen from above and from inside the liquid. Together with
_qs_waterfog(see Volumetric Fog) the depth absorption follows the shifted bottom.
A material refracts when its .qsmat.json entry has the flag MF_REFRACT (see QSMAT):
"WINDOW1": {
"diffuse": "textures/windows/window1_d.png",
"normal": "textures/windows/window1_n.png",
"flags": ["MF_TRANSPARENT", "MF_REFRACT"]
}- The surface has to be translucent:
MF_TRANSPARENTand an alpha channel in thediffuseimage (its alpha is how much of the glass covers what is behind it), the material'salphabelow 1, or a brush entity withalphabelow 1. refractionsets the strength (default 1), e.g."refraction": 0.5for a gentler bend; it makes the materialMF_REFRACT.- The normal map bends the view; without one the glass is flat and bends nothing, which is what flat glass does.
- Glass receives no shadows, it lets the light through. Dynamic lights still light it.
- It needs no worldspawn key;
r_refractionturns it off.
r_refraction (saved, default 1) scales every strength: the map's, a material's refraction and the 1 of
MF_REFRACT. 0 turns refraction off and liquids and glass are blended as without it.
r_refraction_distance (saved, default 1024) is how far away liquids and glass still refract. The strength fades
out over the last quarter of it, so nothing snaps; farther surfaces are blended, which saves a copy of the scene.
At 1024 units the shift is a pixel or two at 1080p. 0 means no limit.
TransparentPass.Renderdecides, withTransparentPass.PlanSceneCopies, where in the sorted back-to-front pass the scene is copied: once before every run of refracting items, and again within a run where the owner changes (TransparentPass.SharesCopy): a brush entity, the world's liquids and the world's glass each get a copy that holds what was drawn before them. A refracting surface replaces its pixels with the copy, so without that a glass wall in front of a pool would erase the pool. It copies the color (PostProcess.copySceneColor) drawn so far, and the depth when it changed (PostProcess.sceneDepthCopy), so whatever was drawn before shows through, other translucent things included. Within one owner nothing is copied again: the faces of the world's liquids do not see each other (water seen through water), which keeps a map full of liquid down to a few copies. A brush entity out of the frustum is not in the pass at all, so glass behind the camera does not get a copy.BrushModelRendererdecides which items refract (refractionOfWorldTurbulentChain,refractionOfWorldBatch,refractionOfEntity) with the same alpha the draw uses, so a surface samples a copy only when one was made for it. The strengths are set for the pass byprepareRefractionand back to 0 after it. Refracting world glass is its own item kind (RefractiveWorldSurface), drawn by thebrush-refractprogram.- The world's liquids and translucent surfaces are packed once per face, in batches shared by the leafs that list them, and handed out once per frame, so a face on a leaf boundary is not blended over itself.
- Within a brush entity, glass is drawn after its other translucent surfaces and does not see them through itself: the scene was copied before the entity.
- The
turbulentandbrush-refractshaders sample the copies throughinclude/refraction.glsl. A shifted sample that lands on something in front of the surface falls back to the unshifted one, so near geometry does not bleed into the liquid. Only the color is copied and refracted: the surface blends its emissive over the unshifted emissive behind it, so bloom of what is behind the surface is kept, just not shifted (bloom blurs it anyway). - A color copy covers only what its items can sample (
TransparentPass.PlanCopyRects): their bounds on the screen, grown by how far each can shift at its strength. Outside of that rectangle the copy holds older pixels, soREFRACTION_MAX_SHIFTinTransparentPass.tsand ininclude/refraction.glslhave to agree, and a shader that shifts further has to say so. An item whose bounds are not known on the screen copies everything: a box with a corner behind the eye, a turned inline model. - The depth copy is shared by the whole transparent pass (
PostProcess.sceneDepthCopy) and made again only after an opaque liquid wrote depth. A full-screen copy costs a blit, a resolve when multisampled; on an integrated GPU at 1080p about 0.2 to 0.5 ms, and copies are most of what refraction costs. TransparentPasstells the brush renderer the distance of each item before it checks or draws it (setRefractionDistance), so the copy plan and the strength in the shader followr_refraction_distancealike.- Refraction needs the scene capture; without it nothing refracts.
R.PreRenderScenerequests it for liquids and fog volumes, and whiler_refractionis above 0 for any map with a refracting material (BrushModelRenderer.HasRefractingMaterials), so glass refracts on a map that has no liquids.