Làm cách nào tôi có thể bù / thu nhỏ một đa giác tam giác trong GLSL?


8

Tôi cần phải bù tất cả các hình tam giác (màu xanh), mỗi hình độc lập với các hình khác, sử dụng công cụ tạo bóng đỉnh. Để điều khiển toàn bộ tam giác, tôi đã tạo các thuộc tính tùy chỉnh (vec3) cho mỗi đỉnh (màu đỏ) đại diện cho các đỉnh lân cận bên trái (màu tím) và bên phải (màu xanh lá cây). Từ điều này, tôi cần rút ra điểm màu cam, cách đều (trong không gian màn hình ) từ cả hai cạnh liền kề. Với ba điểm màu cam như vậy xuất phát từ mỗi tam giác, tam giác đã xử lý (màu cam) được truyền cho bộ đổ bóng mảnh.

mỗi hoạt động đỉnh bù tam giác

Lý tưởng nhất là hình tam giác sẽ bị loại bỏ (như trong backfaces / không được hiển thị) nếu phần bù trừ phủ nhận bất kỳ khoảng trống có sẵn nào trong hình tam giác, chẳng hạn như trong hình tam giác thứ hai trong hình thứ hai.

Tôi đang sử dụng THREE.BufferGeometry () làm cấu trúc dữ liệu của mình.

Dưới đây là ảnh chụp màn hình về hiệu ứng tôi đang hướng tới:

nhập mô tả hình ảnh ở đây


Bạn có thể thêm một chút về bối cảnh rộng hơn? Các hình tam giác bù có được gắn như trong lưới ban đầu không? "Loại bỏ" có nghĩa là tam giác ban đầu bị loại bỏ, hoặc chỉ là phần bù được bỏ đi, để lại hình tam giác ở kích thước ban đầu của nó?
trichoplax

1
Vậy ... làm thế nào để nó hoạt động với lưới? Bởi vì trong một lưới, một đỉnh có nhiều hơn 2 lân cận. Hay đây chỉ là cho tam giác cá nhân?
Nicol Bolas

Việc triển khai của tôi là sao cho tất cả các hình tam giác được đặt trong một bộ đệm liên tục: [P1.x, P1.y, P1.z, P2.x, P2.y, P2.z ... Pn.x, Pn.y, Pn.z] với các điểm lân cận cũng được trình bày rõ ràng tại các thuộc tính. Bằng cách này, mỗi đỉnh của mỗi mặt có thể được tính toán và thao tác mà không ảnh hưởng đến các mặt lân cận. Nicol Bolas, vâng, xử lý từng tam giác riêng biệt.
Jackalope

trichoplax - "Culled" có nghĩa là bị ném ra ngoài, không được hiển thị, như trong một nguyên thủy một mặt, mặt sau.
Jackalope

1
@Jackalope: " Cả hai bạn dường như đề nghị rằng GPU xem các mặt là" buộc "với các mặt khác. " Điều đó bởi vì, nói chung, điều này là đúng. Hầu hết các mắt lưới không chỉ đơn thuần có các tam giác lân cận sử dụng "các thuộc tính giống hệt nhau"; họ sử dụng lại các đỉnh giống nhau . Điều này có thể thông qua các danh sách tam giác sử dụng cùng một chỉ mục nhiều lần, hoặc thông qua các dải tam giác, hoặc bất cứ điều gì. Nhưng nói chung, lưới sử dụng lại các đỉnh lân cận. Các mắt lưới của bạn thì không, nhưng trường hợp cụ thể của bạn không thay đổi trường hợp chung. Đó là lý do tại sao tôi yêu cầu làm rõ.
Nicol Bolas

Câu trả lời:


9

Cho tam giác ▲ ABC, ta chia đôi góc BAC với đường thẳng AD, xuất phát với Định lý góc băm :

BA / BD = CA / CD Sơ đồ hình tam giác Điểm E thể hiện vị trí tinh chỉnh mục tiêu của chúng tôi trên tam giác chèn kết quả mong muốn. Vì nó nằm trên bisector góc AD, nó cách đều nhau từ các cạnh BA & CA, tạo thành các tam giác vuông giống hệt nhau ▲ AFE & ▲ AGE. Bây giờ chúng ta có thể sử dụng Sine cho tam giác vuông để tìm độ dài của AE:

AE = EG / Tội lỗi (EAG)

Đó là tất cả toán học chúng ta cần, vì vậy hãy nấu một số GLSL!

Chúng ta bắt đầu với tất cả các thuộc tính điển hình: ma trận vị trí, bình thường và ma trận biến đổi, nhưng vì trình tạo bóng đỉnh chỉ hoạt động trên một đỉnh duy nhất, chúng ta cần thêm các đỉnh lân cận làm thuộc tính bổ sung. Bằng cách này, mỗi đỉnh sẽ tìm thấy "điểm E" của riêng mình, tạo ra tam giác lồng kết quả. (Lưu ý: Tôi không gọi chúng là "B" & "C" ở đây, vì chúng chưa có trong không gian màn hình .)

    attribute vec3 left; //vertex to the left of this vertex
    attribute vec3 right; //vertex to the right of this vertex

Nói về không gian màn hình, tôi cũng bao gồm tỷ lệ khung hình của màn hình, (và làm cho nó đồng nhất, trong trường hợp cửa sổ được thay đổi kích thước.)

Sau khi chuẩn bị các quy tắc khác nhau cho trình tạo bóng mảnh và biến khuôn mặt thành không gian cắt, chúng ta có thể bắt tay vào công việc áp dụng toán học trên:

        attribute vec3 left; //vertex to the left of this vertex
        attribute vec3 right; //vertex to the right of this vertex
        uniform float aspect;
        varying vec3 vNormal;
        varying vec2 vUv;

        void main() {
            vNormal = normal;
            vUv = uv;

            mat4 xform= projectionMatrix * modelViewMatrix;
            vec4 A = xform * vec4( position, 1.0 );
            vec4 B = xform * vec4( left, 1.0 );
            vec4 C = xform * vec4( right, 1.0 );

            vec3 CB = C.xyz - B.xyz;
            vec2 BA = B.xy - A.xy;
            vec2 CA = C.xy - A.xy;
            float lengthBA = length(BA);
            float lengthCA = length(CA);
            float ratio = lengthBA / ( lengthBA + lengthCA );
            vec3 D = B.xyz + ratio * CB.xyz;
            vec3 AD = D - A.xyz;
            vec3 bisect = normalize(AD);

            float theta = acos( dot(BA, CA) / (lengthBA * lengthCA) ) / 2.0;
            float AE = 1.0/(sin(theta)*aspect);
            newPos.z += AE/length(AD) * (D.z - A.z);
            newPos.x += bisect.x*AE;
            newPos.y += bisect.y*AE;

            gl_Position = newPos;
        }

Mã này cho chúng tôi kết quả dưới đây.

Ảnh chụp màn hình

Lưu ý, có một vài trường hợp cạnh phải làm với các hình tam giác gần như bị lật ngược được lật theo quy trình này và tôi bắt đầu giải quyết vấn đề này trong mã, nhưng bây giờ quyết định chỉ đơn giản là tránh các trường hợp này. Có lẽ tôi sẽ xem lại nó khi tôi hoàn thành dự án này.


1
Công việc tốt đẹp tìm ra điều này! Thực sự thích mô tả toán học lúc đầu.
dùng1118321

0

Điều này có thể đạt được mà không cần các hàm lượng giác bằng cách thu nhỏ vòng tròn của tam giác.

incircle()tính đường tròn của tam giác được tạo bởi các đỉnh A,B,C, nó trả về tâm và bán kính là vec4. Các đỉnh X=A,B,Csau đó được di chuyển vào trong bởi một phần khoảng cách của chúng đến tâm vòng tròn ( Q-X) bằng với tỷ lệ của lề mong muốn so với bán kính bao quanh ( m/Q.w).

vec4 incircle(vec3 A, vec3 B, vec3 C) {
    float a = length(B - C), b = length(C - A), c = length(A - B);
    float abc = a + b + c;
    // http://mathworld.wolfram.com/Incenter.html
    vec3 I = (a * A + b * B + c * C) / abc;
    // http://mathworld.wolfram.com/Inradius.html
    float r = 0.5
            * sqrt((-a + b + c) * (a - b + c) * (a + b - c) / abc);
    return vec4(I, r);
}

vec3 A,B,C; // vertices
float m; // margin
vec4 Q = incircle(A,B,C);
A += clamp(m / Q.w, 0.0, 1.0) * (Q.xyz - A);
B += clamp(m / Q.w, 0.0, 1.0) * (Q.xyz - B);
C += clamp(m / Q.w, 0.0, 1.0) * (Q.xyz - C);

Rất thú vị, Adam! Tôi đã không nghe nói về chức năng này.
Jackalope
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.