Skip to content

Commit cfc84ca

Browse files
committed
take one of new Aperture page
1 parent 5df0e24 commit cfc84ca

File tree

2 files changed

+387
-0
lines changed

2 files changed

+387
-0
lines changed
Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
---
2+
title: Aperture Tutorial (for legacy devs)
3+
description: Temporary documentation for the upcoming Aperture pipeline
4+
tableOfContents: true
5+
sidebar:
6+
label: Legacy -> Aperture Tutorial
7+
order: 1
8+
---
9+
10+
:::note[Note]
11+
This guide is intended for those already familiar with shader development.
12+
:::
13+
14+
:::caution[Warning]
15+
**Aperture is still in early development and not for public use.** Things can and will change, and parts of this section may be incomplete or outright incorrect.
16+
:::
17+
18+
Aperture is a new take on shader packs utilizing TypeScript to create an extensible pipeline.
19+
20+
This serves as temporary documentation whilst Aperture is being developed.
21+
22+
## `pack.ts`
23+
24+
The core of the pipeline; `pack.ts` is where you start registering programs. Unlike in OptiFine/Iris, there is no "*default*" setup; you **must** create one.
25+
26+
The required parts of `pack.ts` are composed of two separate functions: `configureRenderer` and `configurePipeline`.
27+
28+
### `configureRenderer`
29+
30+
`configureRenderer` is where you configure the aspects of how Minecraft's default rendering should change to accomodate your pipeline. This is where you define:
31+
32+
- If you will use shadow maps, and if so, their settings
33+
- If you want sun tilt, ambient occlusion, or directional shade
34+
- If you want point light shadows (documentation not ready)
35+
36+
An example of `configureRenderer`:
37+
38+
```ts
39+
function configureRenderer(config : RendererConfig) : void {
40+
config.disableShade = true;
41+
config.ambientOcclusionLevel = 0.0;
42+
43+
config.render.sun = false;
44+
45+
config.shadow.enabled = true;
46+
config.shadow.resolution = 1024;
47+
}
48+
```
49+
50+
### `configurePipeline`
51+
52+
The actual meat of the pipeline; this is where you will configure the following:
53+
54+
- Textures
55+
- Buffers
56+
- Object shaders (previously known as `gbuffer` programs)
57+
- Command lists (containing composite/compute shaders)
58+
59+
There is no explicit order or way to do this, so we will cover these in order.
60+
61+
First, let's define it.
62+
63+
```ts
64+
function configurePipeline(pipeline : PipelineConfig) : void {
65+
66+
}
67+
```
68+
69+
### Textures
70+
71+
In Aperture, the only textures provided to you by default are depth textures; that being `mainDepthTex` (previously `depthtex0`) and `solidDepthTex` (previously `depthtex1`).
72+
73+
All other textures, including color textures, must be created and provided by you.
74+
75+
For the following, we will use the predefined variables `screenWidth` and `screenHeight`.
76+
77+
Let's create two basic RGBA8 textures. (The reason for creating two will be explained later.)
78+
79+
```ts
80+
let mainTex = pipeline.createTexture('mainTexture') // The string provided here will be what is used to access this texture in shader code.
81+
.format(Format.RGBA8)
82+
.width(screenWidth)
83+
.height(screenHeight)
84+
.build();
85+
86+
let finalTex = pipeline.createTexture('finalTexture')
87+
.format(Format.RGBA8)
88+
.width(screenWidth)
89+
.height(screenHeight)
90+
.build();
91+
```
92+
93+
Remember that we created this, we will use this later.
94+
95+
In Aperture, there are many other types of textures, including array textures, PNG textures, and raw data textures. These will not be covered here.
96+
97+
**Aperture does not contain buffer flipping. You cannot read and write to a texture in the same pass** (unless using `image` variables.)
98+
99+
### Combination pass
100+
101+
Let's get this out of the way first. The "combination pass" is the final stage of rendering, and is required; it will state what is sent as the final image.
102+
103+
Let's make a dummy one quickly.
104+
105+
```ts
106+
pipeline.createCombinationPass("programs/combination.fsh").build();
107+
```
108+
109+
Create `programs/combination.fsh`, and put this in.
110+
111+
```glsl
112+
#version 460 core
113+
114+
uniform sampler2D finalTexture;
115+
in vec2 uv;
116+
117+
layout(location = 0) out vec4 finalColor;
118+
119+
void main() {
120+
finalColor = texture(finalTexture, uv);
121+
}
122+
```
123+
### Object shaders
124+
125+
Previously known as `gbuffer_` shaders, these are the shaders run on world geometry. These differ in many ways from in Optifine/Iris.
126+
127+
Time to define our first shader.
128+
129+
```ts
130+
pipeline.createObjectShader("basic", Usage.BASIC)
131+
.vertex("programs/basic.vsh")
132+
.fragment("programs/basic.fsh")
133+
.target(0, mainTex)
134+
.compile();
135+
```
136+
137+
We have just defined an object shader for the `BASIC` program usage; this will be the shader all others fall back to when there isn't a more specialized alternative.
138+
139+
We have defined that output 0 will write to the `mainTex` we specified earlier.
140+
141+
#### Vertex shader
142+
143+
Time to create the vertex shader. This is quite different than what you are used to.
144+
145+
```glsl
146+
#version 460 core
147+
148+
out vec2 uv;
149+
out vec2 light;
150+
out vec4 color;
151+
152+
void iris_emitVertex(inout VertexData data) {
153+
data.clipPos = iris_projectionMatrix * iris_modelViewMatrix * data.modelPos;
154+
}
155+
156+
void iris_sendParameters(VertexData data) {
157+
uv = data.uv;
158+
light = data.light;
159+
color = vec4(data.color.rgb * data.ao, data.color.a); // Ambient occlusion is split by default in Aperture.
160+
}
161+
```
162+
163+
So. What is going on here? Let's break this down.
164+
165+
First, note the two separate functions. `iris_emitVertex` contains an `inout` copy of the VertexData, hinting that it should be edited, while `iris_sendParameters` does not.
166+
167+
Indeed, the job of `iris_emitVertex` is to *modify the vertex position.* It contains two jobs: modifying the position as the shader wishes, and converting said position to clip space.
168+
169+
Why are these two separate functions? Simply put, because `iris_emitVertex` *can be called more than once per vertex*, while `iris_sendParameters` is guaranteed to *only be called once*. As the names hint, you should send vertex parameters within `sendParameters` for this reason.
170+
However, this is not a hard limit; if needed, you can mix and match these. Just beware the unexpected.
171+
172+
Small note: If you've used Optifine, you may know that you need to use texture matrices to put `light` into 0-1 range. This is not needed in Aperture, and this range is the default range.
173+
174+
#### Fragment shader
175+
176+
Time for the other side.
177+
178+
```glsl
179+
#version 460 core
180+
181+
in vec2 uv;
182+
in vec2 light;
183+
in vec4 color;
184+
185+
layout(location = 0) out vec4 outColor; // Remember when we put location 0 in `pack.ts`? This is where it's used.
186+
187+
void iris_emitFragment() {
188+
vec2 mUV = uv, mLight = light;
189+
vec4 mColor = color;
190+
191+
iris_modifyBase(mUV, mColor, mLight);
192+
193+
outColor = iris_sampleBaseTex(mUV) * iris_sampleLightmap(mLight) * mColor;
194+
195+
if (iris_discardFragment(outColor)) discard;
196+
}
197+
```
198+
199+
Less going on here, but still notable. Let's go through it.
200+
201+
First of all, `main` is now `iris_emitFragment`. Second of all, you must define your outputs; no more `gl_FragData`.
202+
203+
Now, these copies of the outputs; why do we need them? Simply, to allow greater mod support. `iris_modifyBase` is a default hook for mods to modify per-fragment data, without impacting the final image significantly.
204+
205+
(No, you cannot edit the `in` values directly; they're read only.)
206+
207+
**It is always recommended, but not required to have this hook when possible.**
208+
209+
Second, `iris_discardFragment`. Alpha testing (the act of transparent objects being transparent) is not implicit in Aperture; this simple `if` statement takes care of all those situations.
210+
211+
Third, notice the lack of uniforms for sampling the base texture and lightmap. These are handled using built-in functions instead; examples include:
212+
- `iris_sampleBaseTex`
213+
- `iris_sampleLightmap`
214+
- `iris_sampleNormalMap`
215+
- `iris_sampleSpecularMap`
216+
217+
### Command lists (composite/compute)
218+
219+
The functions of composite and compute shaders have been merged into a single "idea", known as a command list. These "lists" run at certain points in the pipeline, similar to `composite`, `deferred`, and `prepare` stages.
220+
221+
Command lists are also capable of having sub-lists, which is helpful for debugging.
222+
223+
Let's create a basic command list with a single composite. Computes will not be covered in this page.
224+
225+
```ts
226+
let postRenderList = pipeline.forStage(Stage.POST_RENDER);
227+
228+
postRenderList.createComposite("brighten")
229+
.fragment("programs/brighten.fsh")
230+
.target(0, finalTex)
231+
.compile();
232+
233+
postRenderList.end();
234+
```
235+
236+
(You are not required to have a vertex shader for composites; although it is allowed, we will not cover them.)
237+
238+
Let's write it! Unlike with object shaders, these don't contain much special syntax.
239+
240+
```glsl
241+
#version 460 core
242+
243+
uniform sampler2D mainTexture;
244+
245+
in vec2 uv;
246+
247+
layout(location = 0) out vec4 finalTex;
248+
249+
void main() {
250+
vec4 col = texture(mainTexture, uv);
251+
252+
finalTex = vec4(pow(col.rgb, 2.2), col.a);
253+
}
254+
```
255+
256+
Computes and buffers will be covered in a separate page. Unlike with composites, they differ greatly in functionality, with buffers being significantly more powerful.

src/content/docs/aperture/oldInfo

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
2+
# -- TODO THE REST --
3+
4+
## Object Shaders
5+
6+
Object shaders require special handling to allow for mod support
7+
8+
### Vertex
9+
Vertex shaders should define two functions:
10+
11+
#### `void iris_emitVertex(inout VertexData data)`
12+
Set `data.clipPos` as you would `gl_Position`. Usually this can just be `iris_projectionMatrix * iris_modelViewMatrix * data.modelPos`
13+
14+
:::note[Info]
15+
`iris_projectionMatrix` and `iris_modelViewMatrix` are patched to make sense for the relevant usage, in the same way `gl_ModelViewMatrix` is in OptiFine.
16+
:::
17+
18+
#### `void iris_sendParameters(VertexData data)`
19+
This is where you can write to your `out` declarations.
20+
21+
Both of these functions make use of the `VertexData` struct.
22+
23+
```glsl
24+
struct VertexData {
25+
vec4 modelPos; // model space position you are given
26+
vec4 clipPos; // clip space position you must set
27+
vec2 uv; // texture coordinate in the atlas
28+
vec2 light; // lightmap texture coordinate - x is blocklight, y is skylight
29+
vec4 color; // vertex color, equivalent to gl_Color
30+
vec3 normal; // model space normal
31+
vec4 tangent; // model space tangent
32+
vec4 overlayColor; // stuff like the red damage flash, equivalent to entityColor
33+
vec3 midBlock; // equivalent to at_midBlock
34+
uint blockId; // hooray
35+
uint textureId; // ???
36+
float ao; // vanilla ambient occlusion
37+
};
38+
```
39+
40+
### Fragment
41+
42+
Fragment shaders are a lot simpler, you just define `void iris_emitFragment()` instead of `void main()`.
43+
44+
## Composite Shaders
45+
46+
### Vertex
47+
48+
Since Aperture uses the core profile, `ftransform()` is not patched. Instead, the position of the vertex and the UV must be manually calculated as follows.
49+
50+
```glsl
51+
#version 450 core
52+
53+
out vec2 uv;
54+
55+
void main() {
56+
vec2 position = vec2(gl_VertexID % 2, gl_VertexID / 2) * 4.0 - 1.0;
57+
58+
uv = (position + 1.0) * 0.5;
59+
60+
gl_Position = vec4(position, 0.0, 1.0);
61+
}
62+
```
63+
64+
## Buffers
65+
66+
The depth textures are `mainDepthTex` and `solidDepthTex`, replacing `depthtex0` and `depthtex1`.
67+
68+
## Uniforms
69+
70+
A list of available uniforms is printed to the console on Iris startup in the form of the structs they are stored in. As an example of how to access these values, `cameraPosition` is now the `pos` member of the `CameraData` struct, and to access it, we would use `ap.camera.pos`. The same pattern can be applied to any struct.
71+
72+
## Shadows
73+
Iris makes use of cascaded shadow maps, which means everything is a lot more complicated.
74+
75+
### Setting Up the Shadow Pass
76+
You do not need to declare the shadow map, as it is automatically defined. If you want any `shadowcolor` style buffers, declare them as an `ArrayTexture`.
77+
78+
### Sampling the Shadow Map
79+
Aperture defines two shadow maps.
80+
- `shadowMap` (equivalent to `shadowtex0`)
81+
- `solidShadowMap` (equivalent to `shadowtex1`)
82+
83+
Both of these should be defined as `sampler2DArray`s. To sample them, you pass the cascade as the `z` component of your coordinate, i.e:
84+
85+
```glsl
86+
float shadow = step(shadowScreenPos.z, texture(shadowMap, vec3(shadowScreenPos.xy, cascade)));
87+
```
88+
89+
Cascade 0 is the smallest, containing only a small area around the player, and cascade 3 is the largest.
90+
91+
Bear in mind that the shadow projection is also an array, because each cascade has a different projection. As such, to sample the shadow map, you must loop through each projection until your position is inside the frustum.
92+
93+
```glsl
94+
int cascade;
95+
for(cascade = 0; cascade < 4; cascade++){
96+
shadowClipPos = shadowProjection[cascade] * shadowViewPos;
97+
98+
if(clamp(shadowClipPos.xy, vec2(-1.0), vec2(1.0)) == shadowClipPos.xy) break;
99+
}
100+
```
101+
102+
### Hardware Filtering
103+
If you want hardware filtering, you can instead use a `sampler2DArrayShadow` and suffix `filtered` to the sampler names (i.e `shadowMapFiltered`. Due to reasons known only to the developers over at Khronos, the cascade is still the `z` component of the coordinate you pass in, so it becomes
104+
105+
```glsl
106+
float shadow = texture(shadowMap, vec4(shadowScreenPos.xy, cascade, shadowScreenPos.z));
107+
```
108+
109+
![](../../../assets/aperture/sampler2darrayshadow.webp)
110+
111+
## Block IDs
112+
The `VertexData` object passed to `iris_sendParameters` has a `blockId` attribute. The following functions can be used on the ID:
113+
114+
```
115+
vec4 iris_getLightColor
116+
uint iris_getMetadata // Raw bitmask, only use if nothing else is enough
117+
bool iris_isFullBlock
118+
bool iris_hasFluid
119+
int iris_getEmission
120+
```
121+
122+
The metadata comes in the form of an 18 bit uint with the following structure
123+
124+
```
125+
0-5: IS_SIDE_SOLID (6 bits) // DOWN, UP, NORTH, SOUTH, WEST, EAST
126+
6-9: EMISSION (4 bits)
127+
10-13: LIGHT_BLOCK (4 bits)
128+
14-16: DIRECTION (3 bits)
129+
17: IS_LOWER (1 bit)
130+
18: IS_FLUID (1 bit)
131+
```

0 commit comments

Comments
 (0)