@mirage-engine/painter 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/mirage-painter.js +4 -4
- package/dist/mirage-painter.umd.js +1 -1
- package/dist/src/BoxGenerator.d.ts +2 -5
- package/dist/src/Painter.d.ts +2 -1
- package/dist/src/dev/devShader.d.ts +5 -0
- package/dist/src/types/common.d.ts +4 -0
- package/package.json +1 -1
- package/src/BoxGenerator.ts +24 -5
- package/src/Painter.ts +3 -2
- package/src/dev/devShader.ts +114 -0
- package/src/types/common.ts +6 -0
package/CHANGELOG.md
CHANGED
package/dist/mirage-painter.js
CHANGED
|
@@ -175,11 +175,11 @@ function E(e, r, o, i, a) {
|
|
|
175
175
|
e.uniforms.uSize.value.set(o, i), b(e.uniforms.uBorderRadius.value, r.borderRadius), e.uniforms.uBorderWidth.value = f(r.borderWidth), e.uniforms.uBgColor.value.copy(n.color), e.uniforms.uBorderColor.value.copy(t.color), e.uniforms.uOpacity.value = r.opacity ?? 1, e.uniforms.uBgOpacity.value = n.alpha, e.uniforms.uBorderOpacity.value = t.alpha, e.uniforms.uTexture && a !== void 0 && (e.uniforms.uTexture.value = a);
|
|
176
176
|
}
|
|
177
177
|
const S = {
|
|
178
|
-
create(e, r, o, i, a, n = 2, t = null) {
|
|
178
|
+
create(e, r, o, i, a, n = 2, t = null, c) {
|
|
179
179
|
if (e === "BOX")
|
|
180
|
-
return R(r, i, a, t);
|
|
180
|
+
return R(r, i, a, t, c);
|
|
181
181
|
if (e === "TEXT") {
|
|
182
|
-
const
|
|
182
|
+
const u = m(
|
|
183
183
|
o || "",
|
|
184
184
|
r,
|
|
185
185
|
i,
|
|
@@ -187,7 +187,7 @@ const S = {
|
|
|
187
187
|
n
|
|
188
188
|
);
|
|
189
189
|
return new l.MeshBasicMaterial({
|
|
190
|
-
map:
|
|
190
|
+
map: u,
|
|
191
191
|
transparent: !0,
|
|
192
192
|
side: l.FrontSide,
|
|
193
193
|
color: 16777215
|
|
@@ -82,4 +82,4 @@
|
|
|
82
82
|
${(t==null?void 0:t.uvModifier)||""}
|
|
83
83
|
`:"",u=n?`
|
|
84
84
|
baseColor = texture2D(uTexture, resultUv);
|
|
85
|
-
`:"",d=(t==null?void 0:t.colorModifier)||"",x=w.replace("#INJECT_DECLARATIONS",a).replace("#INJECT_UV_MODIFIER",l).replace("#INJECT_BASE_COLOR",u).replace("#INJECT_COLOR_MODIFIER",d),f=g(e.backgroundColor),b=g(e.borderColor),B={uSize:{value:new c.Vector2(r,o)},uBorderRadius:{value:new c.Vector4(0,0,0,0)},uBorderWidth:{value:p(e.borderWidth)},uBgColor:{value:f.color},uBorderColor:{value:b.color},uOpacity:{value:e.opacity??1},uBgOpacity:{value:f.alpha},uBorderOpacity:{value:b.alpha},uTexture:{value:null}};return h(B.uBorderRadius.value,e.borderRadius),n&&(B.uTexture.value=i),new c.ShaderMaterial({uniforms:B,vertexShader:M,fragmentShader:x,transparent:!0,side:c.FrontSide})}function T(e,r,o,i,t){const n=g(r.backgroundColor),a=g(r.borderColor);e.uniforms.uSize.value.set(o,i),h(e.uniforms.uBorderRadius.value,r.borderRadius),e.uniforms.uBorderWidth.value=p(r.borderWidth),e.uniforms.uBgColor.value.copy(n.color),e.uniforms.uBorderColor.value.copy(a.color),e.uniforms.uOpacity.value=r.opacity??1,e.uniforms.uBgOpacity.value=n.alpha,e.uniforms.uBorderOpacity.value=a.alpha,e.uniforms.uTexture&&t!==void 0&&(e.uniforms.uTexture.value=t)}const y={create(e,r,o,i,t,n=2,a=null){if(e==="BOX")return O(r,i,t,a);if(e==="TEXT"){const
|
|
85
|
+
`:"",d=(t==null?void 0:t.colorModifier)||"",x=w.replace("#INJECT_DECLARATIONS",a).replace("#INJECT_UV_MODIFIER",l).replace("#INJECT_BASE_COLOR",u).replace("#INJECT_COLOR_MODIFIER",d),f=g(e.backgroundColor),b=g(e.borderColor),B={uSize:{value:new c.Vector2(r,o)},uBorderRadius:{value:new c.Vector4(0,0,0,0)},uBorderWidth:{value:p(e.borderWidth)},uBgColor:{value:f.color},uBorderColor:{value:b.color},uOpacity:{value:e.opacity??1},uBgOpacity:{value:f.alpha},uBorderOpacity:{value:b.alpha},uTexture:{value:null}};return h(B.uBorderRadius.value,e.borderRadius),n&&(B.uTexture.value=i),new c.ShaderMaterial({uniforms:B,vertexShader:M,fragmentShader:x,transparent:!0,side:c.FrontSide})}function T(e,r,o,i,t){const n=g(r.backgroundColor),a=g(r.borderColor);e.uniforms.uSize.value.set(o,i),h(e.uniforms.uBorderRadius.value,r.borderRadius),e.uniforms.uBorderWidth.value=p(r.borderWidth),e.uniforms.uBgColor.value.copy(n.color),e.uniforms.uBorderColor.value.copy(a.color),e.uniforms.uOpacity.value=r.opacity??1,e.uniforms.uBgOpacity.value=n.alpha,e.uniforms.uBorderOpacity.value=a.alpha,e.uniforms.uTexture&&t!==void 0&&(e.uniforms.uTexture.value=t)}const y={create(e,r,o,i,t,n=2,a=null,l){if(e==="BOX")return O(r,i,t,a,l);if(e==="TEXT"){const u=m(o||"",r,i,t,n);return new c.MeshBasicMaterial({map:u,transparent:!0,side:c.FrontSide,color:16777215})}return new c.MeshBasicMaterial({visible:!1})},update(e,r,o,i,t,n,a=2,l){if(r==="BOX")T(e,o,t,n,l);else if(r==="TEXT"){const u=e;u.map&&u.map.dispose();const d=m(i||"",o,t,n,a);u.map=d,u.needsUpdate=!0}}};s.Painter=y,s.createBoxMaterial=O,s.createTextTexture=m,s.updateBoxMaterial=T,Object.defineProperty(s,Symbol.toStringTag,{value:"Module"})});
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
import { BoxStyles } from './types';
|
|
1
|
+
import { BoxStyles, ShaderHooks } from './types';
|
|
2
2
|
import * as THREE from "three";
|
|
3
|
-
export declare function createBoxMaterial(styles: BoxStyles, width: number, height: number, texture?: THREE.Texture | null, hooks?:
|
|
4
|
-
uvModifier?: string;
|
|
5
|
-
colorModifier?: string;
|
|
6
|
-
}): THREE.ShaderMaterial;
|
|
3
|
+
export declare function createBoxMaterial(styles: BoxStyles, width: number, height: number, texture?: THREE.Texture | null, hooks?: ShaderHooks): THREE.ShaderMaterial;
|
|
7
4
|
export declare function updateBoxMaterial(material: THREE.ShaderMaterial, styles: BoxStyles, width: number, height: number, texture?: THREE.Texture | null): void;
|
package/dist/src/Painter.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { ShaderHooks } from './types';
|
|
1
2
|
import * as THREE from "three";
|
|
2
3
|
export declare const Painter: {
|
|
3
|
-
create(type: "BOX" | "TEXT", styles: any, content: string, width: number, height: number, quality?: number, texture?: THREE.Texture | null): THREE.Material;
|
|
4
|
+
create(type: "BOX" | "TEXT", styles: any, content: string, width: number, height: number, quality?: number, texture?: THREE.Texture | null, shaderHooks?: ShaderHooks): THREE.Material;
|
|
4
5
|
update(material: THREE.Material, type: "BOX" | "TEXT", styles: any, content: string, width: number, height: number, quality?: number, texture?: THREE.Texture | null): void;
|
|
5
6
|
};
|
package/package.json
CHANGED
package/src/BoxGenerator.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import * as THREE from "three";
|
|
2
|
-
import { BoxStyles } from "./types";
|
|
2
|
+
import { BoxStyles, ShaderHooks } from "./types";
|
|
3
|
+
// [for dev]
|
|
4
|
+
// import { glassHooks } from "./dev/devShader";
|
|
3
5
|
|
|
4
6
|
function parsePixelValue(value: string | number): number {
|
|
5
7
|
if (typeof value === "number") return value;
|
|
@@ -112,10 +114,8 @@ function parseColor(colorStr: string) {
|
|
|
112
114
|
const g = parseInt(rgbaMatch[2], 10);
|
|
113
115
|
const b = parseInt(rgbaMatch[3], 10);
|
|
114
116
|
const a = rgbaMatch[4] !== undefined ? parseFloat(rgbaMatch[4]) : 1.0;
|
|
115
|
-
|
|
116
117
|
return { color: new THREE.Color(`rgb(${r}, ${g}, ${b})`), alpha: a };
|
|
117
118
|
}
|
|
118
|
-
|
|
119
119
|
return { color: new THREE.Color(colorStr), alpha: 1.0 };
|
|
120
120
|
}
|
|
121
121
|
|
|
@@ -124,9 +124,11 @@ export function createBoxMaterial(
|
|
|
124
124
|
width: number,
|
|
125
125
|
height: number,
|
|
126
126
|
texture: THREE.Texture | null = null,
|
|
127
|
-
hooks?:
|
|
127
|
+
hooks?: ShaderHooks,
|
|
128
128
|
): THREE.ShaderMaterial {
|
|
129
129
|
const hasTexture = texture !== null;
|
|
130
|
+
// [for dev]
|
|
131
|
+
// const activeHooks = hasTexture ? glassHooks : hooks;
|
|
130
132
|
|
|
131
133
|
const declChunk = hasTexture
|
|
132
134
|
? /* glsl */ `
|
|
@@ -135,6 +137,7 @@ export function createBoxMaterial(
|
|
|
135
137
|
`
|
|
136
138
|
: "";
|
|
137
139
|
|
|
140
|
+
// [un dev]
|
|
138
141
|
const uvChunk = hasTexture
|
|
139
142
|
? /* glsl */ `
|
|
140
143
|
vec2 screenUv = (vScreenPos.xy / vScreenPos.w) * 0.5 + 0.5;
|
|
@@ -143,14 +146,30 @@ export function createBoxMaterial(
|
|
|
143
146
|
`
|
|
144
147
|
: "";
|
|
145
148
|
|
|
149
|
+
|
|
150
|
+
// [for dev]
|
|
151
|
+
// const uvChunk = hasTexture
|
|
152
|
+
// ? /* glsl */ `
|
|
153
|
+
// vec2 screenUv = (vScreenPos.xy / vScreenPos.w) * 0.5 + 0.5;
|
|
154
|
+
// vec2 resultUv = screenUv;
|
|
155
|
+
// ${activeHooks?.uvModifier || ""}
|
|
156
|
+
// `
|
|
157
|
+
// : "";
|
|
158
|
+
|
|
146
159
|
const baseColorChunk = hasTexture
|
|
147
160
|
? /* glsl */ `
|
|
148
161
|
baseColor = texture2D(uTexture, resultUv);
|
|
149
162
|
`
|
|
150
163
|
: "";
|
|
151
164
|
|
|
165
|
+
// [un dev]
|
|
152
166
|
const colorModChunk = hooks?.colorModifier || "";
|
|
153
167
|
|
|
168
|
+
// [for dev]
|
|
169
|
+
// const colorModChunk = hasTexture
|
|
170
|
+
// ? activeHooks?.colorModifier || ""
|
|
171
|
+
// : hooks?.colorModifier || "";
|
|
172
|
+
|
|
154
173
|
const fragmentShader = fragmentShaderTemplate
|
|
155
174
|
.replace("#INJECT_DECLARATIONS", declChunk)
|
|
156
175
|
.replace("#INJECT_UV_MODIFIER", uvChunk)
|
|
@@ -194,7 +213,7 @@ export function updateBoxMaterial(
|
|
|
194
213
|
styles: BoxStyles,
|
|
195
214
|
width: number,
|
|
196
215
|
height: number,
|
|
197
|
-
texture?: THREE.Texture | null
|
|
216
|
+
texture?: THREE.Texture | null,
|
|
198
217
|
) {
|
|
199
218
|
const parsedBg = parseColor(styles.backgroundColor);
|
|
200
219
|
const parsedBorder = parseColor(styles.borderColor);
|
package/src/Painter.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as THREE from "three";
|
|
2
|
-
import { BoxStyles, TextStyles } from "./types";
|
|
2
|
+
import { BoxStyles, TextStyles, ShaderHooks} from "./types";
|
|
3
3
|
import { createBoxMaterial, updateBoxMaterial } from "./BoxGenerator";
|
|
4
4
|
import { createTextTexture } from "./TextGenerator";
|
|
5
5
|
|
|
@@ -12,9 +12,10 @@ export const Painter = {
|
|
|
12
12
|
height: number,
|
|
13
13
|
quality: number = 2,
|
|
14
14
|
texture: THREE.Texture | null = null,
|
|
15
|
+
shaderHooks?: ShaderHooks
|
|
15
16
|
): THREE.Material {
|
|
16
17
|
if (type === "BOX") {
|
|
17
|
-
return createBoxMaterial(styles as BoxStyles, width, height, texture);
|
|
18
|
+
return createBoxMaterial(styles as BoxStyles, width, height, texture, shaderHooks);
|
|
18
19
|
} else if (type === "TEXT") {
|
|
19
20
|
const texture = createTextTexture(
|
|
20
21
|
content || "",
|
package/src/dev/devShader.ts
CHANGED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
export const isTraveler = true;
|
|
2
|
+
|
|
3
|
+
export const glassHooks = {
|
|
4
|
+
uvModifier: /* glsl */ `
|
|
5
|
+
// 1. 박스 크기에 비례한 두께감
|
|
6
|
+
// float thickness_uv = min(uSize.x, uSize.y) * 0.5;
|
|
7
|
+
float thickness_uv = 25.0;
|
|
8
|
+
float index = 1.3; // 굴절률 (공기: 1.0, 유리: 1.5~1.9)
|
|
9
|
+
float base_height = thickness_uv * 1.5;
|
|
10
|
+
|
|
11
|
+
// 2. 로컬 SDF 계산 (p는 픽셀 단위)
|
|
12
|
+
vec2 xRadii_uv = mix(uBorderRadius.xw, uBorderRadius.yz, step(0.0, p.x));
|
|
13
|
+
float r_uv = mix(xRadii_uv.y, xRadii_uv.x, step(0.0, p.y));
|
|
14
|
+
float d_uv = sdRoundedBox(p, halfSize, r_uv);
|
|
15
|
+
|
|
16
|
+
// 3. 3D 노말(Normal) 계산
|
|
17
|
+
float dx_uv = dFdx(d_uv);
|
|
18
|
+
float dy_uv = dFdy(d_uv);
|
|
19
|
+
if (abs(dx_uv) < 0.001 && abs(dy_uv) < 0.001) {
|
|
20
|
+
vec2 fallbackDir = normalize(p);
|
|
21
|
+
dx_uv = fallbackDir.x; dy_uv = fallbackDir.y;
|
|
22
|
+
}
|
|
23
|
+
float n_cos_uv = max(thickness_uv + d_uv, 0.0) / max(thickness_uv, 0.001);
|
|
24
|
+
float n_sin_uv = sqrt(max(1.0 - n_cos_uv * n_cos_uv, 0.0));
|
|
25
|
+
vec3 normal_uv = normalize(vec3(dx_uv * n_cos_uv, dy_uv * n_cos_uv, n_sin_uv));
|
|
26
|
+
|
|
27
|
+
// 4. Z 높이 계산
|
|
28
|
+
float h = 0.0;
|
|
29
|
+
if (d_uv < -thickness_uv) {
|
|
30
|
+
h = thickness_uv;
|
|
31
|
+
} else if (d_uv < 0.0) {
|
|
32
|
+
float x = thickness_uv + d_uv;
|
|
33
|
+
h = sqrt(max(thickness_uv * thickness_uv - x * x, 0.0));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// 5. 굴절(Refraction) 적용
|
|
37
|
+
vec3 incident = vec3(0.0, 0.0, -1.0);
|
|
38
|
+
vec3 refract_vec = refract(incident, normal_uv, 1.0 / index);
|
|
39
|
+
|
|
40
|
+
float denom = dot(vec3(0.0, 0.0, -1.0), refract_vec);
|
|
41
|
+
float refract_length = (h + base_height) / max(denom, 0.001);
|
|
42
|
+
|
|
43
|
+
// offsetPx는 '박스 내부의 픽셀 단위' 왜곡 거리입니다.
|
|
44
|
+
vec2 offsetPx = refract_vec.xy * refract_length;
|
|
45
|
+
|
|
46
|
+
// [다시 복구!] 큰 배경 텍스쳐를 자르기 위해 screenUv를 사용
|
|
47
|
+
// 화면 1픽셀이 전체 screenUv(0~1)에서 차지하는 비율을 편미분으로 동적 계산
|
|
48
|
+
vec2 pixelToUv = vec2(dFdx(screenUv.x), dFdy(screenUv.y));
|
|
49
|
+
if (abs(pixelToUv.x) < 0.000001) pixelToUv.x = 1.0 / 1920.0; // 안전 장치
|
|
50
|
+
if (abs(pixelToUv.y) < 0.000001) pixelToUv.y = 1.0 / 1080.0; // 안전 장치
|
|
51
|
+
|
|
52
|
+
// 픽셀 오프셋을 화면 UV 오프셋으로 변환
|
|
53
|
+
vec2 distortOffset = offsetPx * pixelToUv;
|
|
54
|
+
|
|
55
|
+
// vUv 대신 screenUv에서 왜곡값을 빼서 최종 배경 픽셀을 당겨옵니다.
|
|
56
|
+
resultUv = screenUv - distortOffset;
|
|
57
|
+
`,
|
|
58
|
+
|
|
59
|
+
// colorModifier: /* glsl */ `
|
|
60
|
+
// // float thickness_c = min(uSize.x, uSize.y) * 0.5;
|
|
61
|
+
// float thickness_c = 25.0;
|
|
62
|
+
// float dx_c = dFdx(d);
|
|
63
|
+
// float dy_c = dFdy(d);
|
|
64
|
+
// if (abs(dx_c) < 0.001 && abs(dy_c) < 0.001) {
|
|
65
|
+
// vec2 fallbackDir = normalize(p);
|
|
66
|
+
// dx_c = fallbackDir.x; dy_c = fallbackDir.y;
|
|
67
|
+
// }
|
|
68
|
+
// float n_cos_c = max(thickness_c + d, 0.0) / max(thickness_c, 0.001);
|
|
69
|
+
// float n_sin_c = sqrt(max(1.0 - n_cos_c * n_cos_c, 0.0));
|
|
70
|
+
// vec3 normal_c = normalize(vec3(dx_c * n_cos_c, dy_c * n_cos_c, n_sin_c));
|
|
71
|
+
|
|
72
|
+
// float edgeReflection = smoothstep(-5.0, 0.0, d) * smoothstep(0.0, -5.0, d);
|
|
73
|
+
// float fresnel = pow(1.0 - max(dot(normal_c, vec3(0.0, 0.0, 1.0)), 0.0), 3.0);
|
|
74
|
+
|
|
75
|
+
// finalColor.rgb += vec3(1.0) * (edgeReflection * 0.4 + fresnel * 0.5);
|
|
76
|
+
// `,
|
|
77
|
+
colorModifier: /* glsl */ `
|
|
78
|
+
// 1. 노말 계산 (기존과 동일)
|
|
79
|
+
float thickness_c = 25.0;
|
|
80
|
+
float dx_c = dFdx(d);
|
|
81
|
+
float dy_c = dFdy(d);
|
|
82
|
+
if (abs(dx_c) < 0.001 && abs(dy_c) < 0.001) {
|
|
83
|
+
vec2 fallbackDir = normalize(p);
|
|
84
|
+
dx_c = fallbackDir.x; dy_c = fallbackDir.y;
|
|
85
|
+
}
|
|
86
|
+
float n_cos_c = max(thickness_c + d, 0.0) / max(thickness_c, 0.001);
|
|
87
|
+
float n_sin_c = sqrt(max(1.0 - n_cos_c * n_cos_c, 0.0));
|
|
88
|
+
vec3 normal_c = normalize(vec3(dx_c * n_cos_c, dy_c * n_cos_c, n_sin_c));
|
|
89
|
+
|
|
90
|
+
// ==========================================
|
|
91
|
+
// [애플 스타일 리퀴드 글래스 틴트 & 질감 추가]
|
|
92
|
+
// ==========================================
|
|
93
|
+
|
|
94
|
+
// A. 틴트(Tint) 조작
|
|
95
|
+
// 밝은 테마라면 vec3(1.0), 어두운 테마라면 vec3(0.1) 등을 사용
|
|
96
|
+
vec3 tintColor = vec3(0.03, 0.03, 0.8);
|
|
97
|
+
float tintStrength = 0.3; // 틴트 강도 (0.1 ~ 0.3 사이 추천)
|
|
98
|
+
finalColor.rgb = mix(finalColor.rgb, tintColor, tintStrength);
|
|
99
|
+
|
|
100
|
+
// B. 프로스트 글래스 노이즈 (시각적 밀도감)
|
|
101
|
+
// vUv를 이용한 간단한 난수 생성으로 화면 픽셀 단위의 미세한 질감을 줌
|
|
102
|
+
float noise = fract(sin(dot(vUv, vec2(12.9898, 78.233))) * 43758.5453) * 2.0 - 1.0;
|
|
103
|
+
finalColor.rgb += noise * 0.01; // 노이즈 강도 (너무 강하면 지저분해짐)
|
|
104
|
+
|
|
105
|
+
// C. 프레넬(Fresnel) 및 날카로운 단면 반사광
|
|
106
|
+
float fresnel = pow(1.0 - max(dot(normal_c, vec3(0.0, 0.0, 1.0)), 0.0), 4.0);
|
|
107
|
+
|
|
108
|
+
// 기존의 두꺼운 15px 빛 맺힘 대신 1.5px~2px 수준의 예리한 테두리 하이라이트
|
|
109
|
+
float sharpEdge = smoothstep(-5.0, 0.0, d) * smoothstep(0.0, -5.0, d);
|
|
110
|
+
|
|
111
|
+
// 빛 합성 (테두리가 살짝 빛나며 유리 질감을 완성)
|
|
112
|
+
finalColor.rgb += vec3(1.0) * (sharpEdge * 0.6 + fresnel * 0.3);
|
|
113
|
+
`,
|
|
114
|
+
};
|