Загрузка данных
# TASK
Rewrite the WATER system of my Iris shader pack (Minecraft 1.21.1, NeoForge, Iris). Water currently looks like vanilla. Reply in Russian, code comments in English. Show FULL files, no "..." placeholders. Do not touch unrelated files.
Before you start: read ALL files in /shaders. List which files you will change. Do the steps IN ORDER and do not skip any.
# REFERENCES (use them for exact names and formulas)
- shaders.properties docs: https://shaders.properties/
- OptiFine shader spec (Iris-compatible): https://github.com/sp614x/optifine/blob/master/OptiFineDoc/doc/shaders.txt
- Iris repo: https://github.com/IrisShaders/Iris
- Water physics and waves: https://developer.nvidia.com/gpugems/gpugems/part-i-natural-effects/chapter-1-effective-water-simulation-physical-models
- Screen-space reflections (simple explanation): https://lettier.github.io/3d-game-shaders-for-beginners/screen-space-reflection.html
- Screen-space ray tracing (theory): https://jcgt.org/published/0003/04/04/
- Fresnel (Schlick): https://en.wikipedia.org/wiki/Schlick%27s_approximation
- Beer-Lambert absorption: https://en.wikipedia.org/wiki/Beer%E2%80%93Lambert_law
- GGX specular: https://learnopengl.com/PBR/Theory
# HARD RULES
- `#version 330 compatibility` on the FIRST line of every file.
- Linear color space everywhere. sRGB conversion only in final.fsh.
- Every `varying` in .vsh must exist with the same type/name in .fsh.
- Declare every uniform before use. Do not invent uniform names; use only standard OptiFine/Iris ones.
- Every feature has a #define in lib/settings.glsl and an entry in shaders.properties screen "Water" with names in lang/en_us.lang and ru_ru.lang.
- Do NOT use the vanilla water texture color. Only use gl_Color (biome tint) as an optional multiplier.
- If something is not supported or you are unsure, SAY SO instead of guessing.
# BUFFER LAYOUT (use exactly this, document it in a comment at the top of lib/settings.glsl)
- colortex0 (RGBA16F): scene color (HDR, linear)
- colortex1 (RGBA16): view-space normal encoded as n*0.5+0.5 (rgb), a = 1.0
- colortex2 (RGBA16): r = water mask (1.0 = water, 0.0 = not water), g = roughness, b = sky lightmap (lm.y), a = 1.0
Declare formats in composite.fsh and final.fsh:
const int colortex0Format = RGBA16F;
const int colortex1Format = RGBA16;
const int colortex2Format = RGBA16;
IMPORTANT BLENDING FIX: water is translucent, so alpha blending would corrupt colortex1 and colortex2. In shaders.properties add:
blend.gbuffers_water.colortex1=off
blend.gbuffers_water.colortex2=off
Also always write alpha = 1.0 to colortex1 and colortex2.
# STEP 0 - DEBUG (do first)
Add #define DEBUG_WATER_MODE in settings.glsl: 0=off, 1=solid red water, 2=show water mask in composite, 3=show water normals in composite. I must be able to verify each stage visually.
# STEP 1 - WATER ID
block.properties:
block.10002 = minecraft:water
(Also optional: block.10003 = minecraft:ice minecraft:blue_ice minecraft:packed_ice for later.)
gbuffers_water.vsh must read `attribute vec4 mc_Entity;` and pass `varying float isWater;` with: isWater = (int(mc_Entity.x + 0.5) == 10002) ? 1.0 : 0.0;
Stained glass, ice etc. are also drawn in gbuffers_water, so ONLY apply water effects when isWater > 0.5. Everything else keeps normal rendering (vanilla texture, mask = 0).
# STEP 2 - gbuffers_water.vsh
Pass to fragment: texcoord, glcolor, lmcoord, worldPos, isWater.
Reference code:
```glsl
#version 330 compatibility
uniform mat4 gbufferModelViewInverse;
uniform vec3 cameraPosition;
attribute vec4 mc_Entity;
varying vec2 texcoord;
varying vec2 lmcoord;
varying vec4 glcolor;
varying vec3 worldPos;
varying float isWater;
void main() {
vec4 viewPos = gl_ModelViewMatrix * gl_Vertex;
worldPos = (gbufferModelViewInverse * viewPos).xyz + cameraPosition;
texcoord = (gl_TextureMatrix[0] * gl_MultiTexCoord0).xy;
lmcoord = (gl_TextureMatrix[1] * gl_MultiTexCoord1).xy;
glcolor = gl_Color;
isWater = (int(mc_Entity.x + 0.5) == 10002) ? 1.0 : 0.0;
gl_Position = gl_ProjectionMatrix * viewPos;
}
```
(In Iris lightmap coords must be remapped: lmcoord = (lmcoord - 1.0/32.0) / (30.0/32.0) clamp to 0..1.)
# STEP 3 - gbuffers_water.fsh (procedural water surface)
Algorithm:
1. Wave height function h(xz, t) = sum of 3-4 layers of value noise / sine waves at different scales and directions, using frameTimeCounter. WATER_WAVE_HEIGHT slider scales it.
2. Normal by finite differences: e = 0.05; dx = h(p+vec2(e,0)) - h(p-vec2(e,0)); dz = h(p+vec2(0,e)) - h(p-vec2(0,e)); worldNormal = normalize(vec3(-dx/(2e), 1.0, -dz/(2e))).
3. viewNormal = normalize(mat3(gbufferModelView) * worldNormal) (declare uniform mat4 gbufferModelView).
4. Base color: deep blue-green, e.g. vec3(0.02, 0.12, 0.18) * glcolor.rgb optional. Alpha 0.55-0.8. Multiply by sky lightmap so caves are dark.
5. Output:
```glsl
/* DRAWBUFFERS:012 */
gl_FragData[0] = vec4(waterColor, waterAlpha);
gl_FragData[1] = vec4(viewNormal * 0.5 + 0.5, 1.0);
gl_FragData[2] = vec4(1.0, 0.02, lmcoord.y, 1.0); // mask, roughness, skylight
```
For non-water translucent blocks (isWater < 0.5): output vanilla texture*glcolor to colortex0, and mask = 0 in colortex2.
Hash noise helper to use (no texture needed):
```glsl
float hash(vec2 p){ return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); }
float vnoise(vec2 p){ vec2 i=floor(p), f=fract(p); f=f*f*(3.0-2.0*f);
return mix(mix(hash(i),hash(i+vec2(1,0)),f.x), mix(hash(i+vec2(0,1)),hash(i+vec2(1,1)),f.x), f.y); }
```
# STEP 4 - composite.fsh (reflections, refraction, absorption)
Uniforms needed: colortex0, colortex1, colortex2, depthtex0, depthtex1, gbufferProjection, gbufferProjectionInverse, gbufferModelViewInverse, shadowLightPosition, skyColor, fogColor, viewWidth, viewHeight, frameTimeCounter.
Only process pixels where texture(colortex2, uv).r > 0.5. Others: pass colortex0 through unchanged.
Helper:
```glsl
vec3 viewPosFromDepth(vec2 uv, float depth){
vec4 ndc = vec4(vec3(uv, depth) * 2.0 - 1.0, 1.0);
vec4 v = gbufferProjectionInverse * ndc;
return v.xyz / v.w;
}
```
4a. Refraction: offset = viewNormal.xy * 0.03 * REFRACTION_STRENGTH; sample colortex0 at uv+offset ONLY if colortex2.r at that offset is water or depth is valid, otherwise use uv (prevents leaking edges).
4b. Absorption (Beer-Lambert): waterDepth = distance(viewPos(depthtex0), viewPos(depthtex1)); absorb = exp(-vec3(0.45, 0.09, 0.06) * waterDepth * WATER_ABSORPTION); underwaterColor = sceneBehind * absorb + deepColor * (1.0 - absorb).
4c. Fresnel: cosT = clamp(dot(-normalize(viewPos), viewNormal), 0.0, 1.0); F = 0.02 + 0.98 * pow(1.0 - cosT, 5.0).
4d. SSR (view-space ray march, use depthtex1 for hits so water does not reflect itself):
```glsl
vec4 ssr(vec3 viewPos, vec3 n){
vec3 dir = normalize(reflect(normalize(viewPos), n));
if (dir.z > 0.0) return vec4(0.0); // ray goes toward camera
float stepLen = 0.4;
vec3 p = viewPos;
for (int i = 0; i < SSR_STEPS; i++) { // 16 low, 32 medium, 64 high
p += dir * stepLen;
vec4 pr = gbufferProjection * vec4(p, 1.0);
pr.xyz /= pr.w;
vec2 suv = pr.xy * 0.5 + 0.5;
if (suv.x < 0.0 || suv.x > 1.0 || suv.y < 0.0 || suv.y > 1.0 || pr.z > 1.0) break;
float d = texture2D(depthtex1, suv).r;
vec3 sp = viewPosFromDepth(suv, d);
float diff = sp.z - p.z; // >0 means ray is behind surface
if (diff > 0.0 && diff < stepLen * 2.0) {
// binary refinement: 5 iterations, halving step back and forth
// ... implement fully ...
float edge = smoothstep(0.0, 0.1, min(min(suv.x, 1.0-suv.x), min(suv.y, 1.0-suv.y)));
return vec4(texture2D(colortex0, suv).rgb, edge);
}
stepLen *= 1.15;
}
return vec4(0.0);
}
```
(Implement the binary refinement fully, do not leave a comment.)
4e. Sky fallback: if SSR misses (alpha 0), reflection = skyGradient(reflectedDirWorld) based on skyColor/fogColor, multiplied by sky lightmap (colortex2.b) so caves do not reflect the sky.
4f. Final water pixel: color = mix(underwaterColor, reflection, F * WATER_REFLECTION_STRENGTH) + sunSpecular.
Sun specular: GGX or Blinn-Phong with H = normalize(L + V), L = normalize(shadowLightPosition), very sharp (roughness 0.02), multiplied by sky lightmap and by (1 - rainStrength).
4g. Rain: increase wave height and reduce reflection slightly when rainStrength > 0.
# STEP 5 - OPTIONS (screen "Water")
Sliders: WATER_WAVE_HEIGHT (0.2-2.0), WATER_ABSORPTION (0.5-3.0), REFRACTION_STRENGTH (0-2), WATER_REFLECTION_STRENGTH (0-1), SSR_STEPS (16,32,64), WATER_COLOR_R/G/B.
Add Low/Medium/High/Ultra profiles in shaders.properties that set SSR_STEPS and wave layer count.
# STEP 6 - SELF-CHECK (output this checklist at the end, answer each point yes/no)
- All files start with #version 330 compatibility?
- Every varying matches between vsh and fsh?
- DRAWBUFFERS comment matches the number of gl_FragData writes?
- blend.gbuffers_water.colortex1/2=off added?
- block.properties water ID equals the ID checked in vsh?
- Debug modes 1, 2, 3 work?
- No placeholder comments left like "implement here"?
Then tell me in Russian how to test: press R in the Iris shader menu, set DEBUG_WATER_MODE=1 first, then 2, then 3, then 0.