first commit

This commit is contained in:
2026-10-06 12:16:46 +09:00
commit 619d980098
415 changed files with 22283 additions and 0 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

+32
View File
@@ -0,0 +1,32 @@
uniform sampler2D uSceneTexture;
uniform mat4 uContentViewProjection,uContentInverseViewProjection;
uniform vec3 uContentCameraPosition,uLightPosition,uBackgroundColor;
uniform float uGlassZ,uGridSize,uStrength,uMosaicStyle,uCurvature,uGlossStrength;
uniform vec2 uViewOffset,uPetSize,uPaneSize;
uniform vec4 uPaneRect;
varying vec2 vUv;
vec2 projectUv(vec3 p){vec4 c=uContentViewProjection*vec4(p,1.);float w=abs(c.w)<.0001?(c.w<0.?-.0001:.0001):c.w;return c.xy/w*.5+.5;}
vec4 sampleScene(vec2 uv){return texture2D(uSceneTexture,clamp(uv,0.,1.));}
vec4 samplePane(vec2 pane){return sampleScene(projectUv(vec3((pane-.5)*uPaneSize,uGlassZ)));}
float hash(vec2 p){return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5453);}
vec2 hash2(vec2 p){return vec2(hash(p),hash(p+vec2(19.19,73.73)));}
float safeZ(float z){return abs(z)<.0001?(z<0.?-.0001:.0001):z;}
vec3 bend(vec3 i,vec3 n){vec3 r=refract(i,n,.75);return length(r)<.0001?i:r;}
float softLimit(float value,float limit){return value*limit/(limit+abs(value));}
void main(){
if(uStrength<=.001){gl_FragColor=sampleScene(vUv);
#include <colorspace_fragment>
return;}
vec4 f=uContentInverseViewProjection*vec4(vUv*2.-1.,1.,1.);float fw=abs(f.w)<.0001?(f.w<0.?-.0001:.0001):f.w;vec3 farWorld=f.xyz/fw;vec3 i=normalize(farWorld-uContentCameraPosition);float gt=(uGlassZ-uContentCameraPosition.z)/safeZ(i.z);vec3 gh=uContentCameraPosition+i*gt;
vec2 paneUv=gh.xy/uPaneSize+.5;vec2 grid=vec2(uGridSize,max(1.,uGridSize*uPaneSize.y/uPaneSize.x));vec2 gridUv=paneUv*grid,cell=floor(gridUv),local=fract(gridUv)-.5,cellSize=1./grid,random=hash2(cell);float style=clamp(uMosaicStyle,0.,1.),cellWorld=min(uPaneSize.x/grid.x,uPaneSize.y/grid.y);
if(paneUv.x<0.||paneUv.x>1.||paneUv.y<0.||paneUv.y>1.){gl_FragColor=vec4(uBackgroundColor,1.);
#include <colorspace_fragment>
return;}
// Positive height bulges toward the observer (+Z); negative height is concave.
float curvature=clamp(uCurvature,-1.,1.),excess=max(curvature-.65,0.),surfaceCurvature=curvature-(.25/.35)*excess*excess; float height=.009*cellWorld*surfaceCurvature*mix(.92,1.08,hash(cell+vec2(41.7,17.3))); vec2 q=2.*local; vec2 q2=q*q,q3=q2*q,b=1.-q2*q2; vec2 skew=(hash2(cell+vec2(23.8,61.2))-.5)*.28; float asym=1.+dot(skew,q); vec2 dhdq=height*vec2(-4.*q3.x*b.y*asym+b.x*b.y*skew.x,-4.*q3.y*b.x*asym+b.x*b.y*skew.y); vec2 cellDimensions=uPaneSize/grid; vec2 grad=-2.*dhdq/cellDimensions; vec3 nCurved=normalize(vec3(grad+(random-.5)*.012,1.)),nFacet=normalize(vec3((random-.5)*.012,1.));
vec3 straightHit=gh+i*(-gh.z/safeZ(i.z));vec3 facetRay=bend(i,nFacet),curvedRay=bend(i,nCurved);vec3 facetHit=gh+facetRay*(-gh.z/safeZ(facetRay.z)),curvedHit=gh+curvedRay*(-gh.z/safeZ(curvedRay.z));vec2 base=facetHit.xy-straightHit.xy,curve=curvedHit.xy-facetHit.xy;vec2 worldDelta=vec2(softLimit(base.x,cellWorld*.08),softLimit(base.y,cellWorld*.08));vec2 d=curve/cellWorld;worldDelta+=vec2(.3*d.x/(.3+abs(d.x)),.3*d.y/(.3+abs(d.y)))*cellWorld;
vec2 sourceCenter=(cell+.5)/grid+(random-.5)*.32*cellSize+worldDelta/uPaneSize;vec2 widths;if(style>.5){widths=mix(vec2(.82),vec2(1.12),hash2(cell+vec2(11.4,9.8)));}else{float widthSeed=hash(cell+vec2(53.1,27.4));if(widthSeed<.6)widths=mix(vec2(.90),vec2(1.08),hash2(cell+vec2(7.2,3.1)));else if(widthSeed<.9)widths=mix(vec2(.46),vec2(.70),hash2(cell+vec2(7.2,3.1)));else widths=mix(vec2(1.10),vec2(1.28),hash2(cell+vec2(7.2,3.1)));}float angle=(random.x-.5)*.08,c=cos(angle),s=sin(angle);vec2 sampleUv=sourceCenter+mat2(c,-s,s,c)*(local*cellSize)*widths;
float edgeDistance=min(min(paneUv.x,paneUv.y),min(1.-paneUv.x,1.-paneUv.y));float edgeWeight=smoothstep(0.,max(cellSize.x,cellSize.y)*.75,edgeDistance);float strength=clamp(uStrength,0.,1.)*edgeWeight;vec2 blurStep=cellSize*mix(.006,.012,hash(cell+vec2(5.)));vec2 centerUv=mix(paneUv,sampleUv,strength);vec4 color=samplePane(centerUv)*.4;color+=samplePane(mix(paneUv,sampleUv+vec2(blurStep.x,0.),strength))*.15;color+=samplePane(mix(paneUv,sampleUv-vec2(blurStep.x,0.),strength))*.15;color+=samplePane(mix(paneUv,sampleUv+vec2(0.,blurStep.y),strength))*.15;color+=samplePane(mix(paneUv,sampleUv-vec2(0.,blurStep.y),strength))*.15;color.a=1.;
vec3 v=normalize(uContentCameraPosition-gh),reflected=reflect(-v,nCurved); float lightT=(uLightPosition.z-gh.z)/safeZ(reflected.z); vec2 lightHit=gh.xy+reflected.xy*lightT; vec2 halfSize=vec2(.32,.42)*min(uPaneSize.x,uPaneSize.y); vec2 windowCoord=abs(lightHit-uLightPosition.xy)/halfSize; vec2 windowMask=1.-smoothstep(vec2(.45),vec2(1.15),windowCoord); float spec=windowMask.x*windowMask.y*step(.0001,reflected.z)*step(0.,lightT); float gloss=spec*clamp(uGlossStrength,0.,1.)*.12*edgeWeight*clamp(uStrength,0.,1.); color.rgb=mix(color.rgb,vec3(1.),gloss); gl_FragColor=color;
#include <colorspace_fragment>
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

+13
View File
@@ -0,0 +1,13 @@
# 자연스러운 셀 경계 1차 보정
2026-09-13. 사용자 구현 승인. karpathy-guidelines 적용.
가정: 레퍼런스2의 큰 눈·코 조각을 유지하면서 cut-paste 인상을 줄인다. 전면적인 물리 유리 재구현이 아닌 기존 Mosaic 최소 수정이다. 비교 구도는 기본 샘플 2.4/-.10/-.35/Grid10/.90/곡률+.65 및 얼굴 근접3/-.15/-.35/Grid8/.90/곡률+.65/광택0.
첫 후보: 곡면에 의한 worldDelta를 셀의 회전/축별 배율 변환 내부에서 처리한다. 셀 가장자리6.5% 구간에서 최종 UV 변위를 smoothstep으로0에 수렴시킨다. 원본 색과 효과 색을 이중 혼합하지 않는다. 기존 seed/샘플 폭/곡면 법선/반사/5tap 필터/Distortion0/외곽처리는 유지한다.
리스크: 좁은 구간에 변형이 집중되면 늘어진 띠나 두꺼운 격자가 보일 수 있다. 실제 화면 비교 후 폭을 조정하거나 방식을 재검토한다. 연속적인 UV가 물리적으로 정확한 굴절을 의미하지는 않는다.
검증: 변경 전 Glass OFF 부위별 카메라 시차 → 같은 구도의 전후 정지 비교 → 광택0에서 곡률0,±.65,±1 비교 → 드래그 유리좌표 → 모바일 → 기존 효과/Distortion0와 shader error → TypeScript/Vite build. 영상/실기기gyro/성능 측정은 별도.
기존 셰이더는 before.frag, 기준 이미지 before-default.png / before-close.png로 보존했다. 제품 기본값 변경, 미디어 게시, AI depth 추가는 하지 않는다.
Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

+41
View File
@@ -0,0 +1,41 @@
# 자연스러운 셀 경계 1차 구현 결과
2026-09-13. Astra 설계 및 최종 정적/이미지 검토, Luna 소스 구현, 주 에이전트 실제 브라우저 검증. 적용 스킬 karpathy-guidelines.
## 변경
소스 변경은 src/shaders/mosaic.frag 한 파일이다.
- 곡면에 따른 worldDelta를 셀 로컬 좌표에 더한 후 기존 회전/축별 배율을 적용한다. 굴절 변위가 셀 확대·압축과 함께 변한다.
- 셀별 독립 이동 계수 .32→.16. 최대 이동 ±.16셀에서 ±.08셀로 감소. 기존 큰 확대/압축의 seed와 widths 분포는 보존.
- 셀 경계에서 rawSampleUv의 변위를 paneUv로 수렴시킨다. 최종 전이폭은 각 축 .085셀. 첫 .065 후보에서 가는 뾰족한 이음새를 보고 jitter 감소 후 폭을 조정했다.
- 색상 두 겹 혼합이나 전체 블러 증가 없이 UV 변위를 연결한다. 추가 texture fetch 없음.
- 기본 굴곡+.65, signed 높이/공유 법선, 사각 창 반사, 5tap 필터, outer pane fade, Distortion0 우회, Depth/RT/UI는 유지했다.
## 시각 결과
동일 사진/동일 구도 before-default→after-default, before-close→after-close 비교. 이마·뺨·목의 일부 사각 절단이 완만해지고 큰 눈·코·입 조각을 유지한다. close는 광택0이므로 반사로 가린 개선이 아니다. Astra 독립 이미지 검토도 1차 변경 수용 가능으로 판단했다.
레퍼런스 수준의 완전한 유리 질감은 아직 아니다. 오른눈을 가로지르는 가는 선, 입/목과 상단 검은 배경의 일부 이음새가 남는다. 최종 UV 전이는 렌즈의 물리 표면을 정확히 재구성한 것이 아니며, 기존 반사 법선에 이 전이 자체를 반영한 것도 아니다. 곡면·배율 연결의 시각 근사이고 후속 개선 여지가 있다. 촘촘 유리에서도 세부가 유지되나 배경 격자 반복은 보인다.
## 실행한 검증
- 변경 전 Glass OFF: cameraX0→.095, 코531.509→528.716px, 왼눈459.283→457.506px, 몸350.477→350.471px. 부위별 카메라 시차 확인. 수작업 테스트 Depth이며 AI 추정 아님.
- 1280×720 기본 샘플 확대2.4/X-.10/Y-.35/Grid10/Distortion.90/굴곡+.65/광택.30 전후.
- 근접 확대3/X-.15/Y-.35/Grid8/Distortion.90/굴곡+.65/광택0 전후.
- 최종 근접 화면에서 광택0, 곡률0/-1/+1 시각 비교. 평탄에서도 셀 확대/이동이 남고 부호에 따른 윤곽 변화가 나타남. 중간 후보에서는 -.65도 비교했다.
- 390×844 최종 근접 화면: 눈·코 식별, 드래그 시 액자/유리 경계 함께 유지. cameraX .127 관측. 최대 이동 한계에 도달했다고 주장하지 않는다. 뷰포트 override 복원.
- Distortion0에서 기존 무효과 출력 확인. 사진이 액자 밖까지 보이는 기존 계약/한계는 유지된다.
- Mosaic 조각/촘촘 스타일, Reeded, Frosted 전환과 정면 화면 확인.
- 최종 브라우저 error logs 빈 배열.
- Luna 최종 npm.cmd run build 통과: TypeScript 및 Vite. 자동 시각 테스트나 성능 테스트 통과를 의미하지 않는다.
미검증: 영상 재생, 실제 모바일 센서/터치, GPU별 호환성, FPS와 장시간 메모리. 새 반사 모델/방향성 퍼짐은 이번에 추가하지 않았다. Astra는 이번 범위에서 더 많은 변수를 동시에 바꾸지 않고 비교 가능한 1차 결과로 유지하는 것을 권했다.
## 재현과 보존
npm.cmd run dev -- --host 127.0.0.1 후 http://127.0.0.1:5173/ 에서 샘플 로드. 기본 또는 위 근접값 적용.
브라우저 전달 화면은 근접값, 광택.15, 굴곡+.65, Camera Offset.30/감도1의 정면이다. 저장된 after-close는 비교용 광택0, after-mobile은 광택.30이다. 원본 사진/Depth 변경 및 외부 게시 없음. 소스 기본값은 변경하지 않았다.
before.frag와 before-*.png는 작업 전, candidate-*.png는 .32/.065 후보, tuned-*.png는 .16/.065 후보, after-*.png는 최종 .16/.085 결과다. Git 저장소를 사용할 수 없어 원본 파일 백업과 직접 대조로 변경을 검토했다.