2013-07-01 32 views
23

Điểm sprites là sự lựa chọn tốt nhất để xây dựng một hệ thống hạt?Điểm Sprites cho hệ thống hạt

Các sprites điểm có trong các phiên bản mới hơn của OpenGL và trình điều khiển của cạc đồ họa mới nhất không? Hoặc tôi nên làm điều đó bằng cách sử dụng vbo và glsl?

Trả lời

86

Điểm sprites thực sự rất phù hợp với hệ thống hạt. Nhưng họ không có liên quan gì đến VBO và GLSL, nghĩa là chúng là một tính năng hoàn toàn trực giao. Không có vấn đề gì nếu bạn sử dụng sprites điểm hay không, bạn luôn phải sử dụng VBO để tải hình học lên, chúng chỉ là điểm, họa tiết được tạo sẵn hoặc bất cứ thứ gì và bạn luôn phải đặt hình học này thông qua bộ bộ đổ bóng (trong OpenGL hiện đại tất nhiên).

Đó là các điểm sprites được cho là được hỗ trợ rất tốt trong OpenGL hiện đại, không chỉ tự động như với phương pháp cố định chức năng cũ. Những gì không được hỗ trợ là các tính năng giảm điểm cho phép bạn mở rộng kích thước của điểm dựa trên khoảng cách của nó đến máy ảnh, bạn phải làm điều này bằng tay bên trong đổ bóng đỉnh. Trong cùng một cách, bạn phải làm texturing của điểm một cách thủ công trong một shader fragment thích hợp, sử dụng biến đầu vào đặc biệt gl_PointCoord (có nghĩa là trong [0,1] -square của toàn bộ điểm mà đoạn hiện tại là). Ví dụ một điểm đường ống sprite cơ bản có thể nhìn theo cách này:

... 
glPointSize(whatever);    //specify size of points in pixels 
glDrawArrays(GL_POINTS, 0, count); //draw the points 

vertex:

uniform mat4 mvp; 

layout(location = 0) in vec4 position; 

void main() 
{ 
    gl_Position = mvp * position; 
} 

fragment shader:

uniform sampler2D tex; 

layout(location = 0) out vec4 color; 

void main() 
{ 
    color = texture(tex, gl_PointCoord); 
} 

Và đó là tất cả. Tất nhiên những shaders chỉ làm bản vẽ cơ bản nhất của sprites kết cấu, nhưng là một điểm khởi đầu cho các tính năng hơn nữa. Ví dụ để tính toán kích thước của sprite dựa trên khoảng cách của nó tới máy ảnh (có thể để cho nó kích thước không gian thế giới cố định), bạn phải glEnable(GL_PROGRAM_POINT_SIZE) và ghi vào biến đầu ra đặc biệt gl_PointSize trong bóng đổ vertex:

uniform mat4 modelview; 
uniform mat4 projection; 
uniform vec2 screenSize; 
uniform float spriteSize; 

layout(location = 0) in vec4 position; 

void main() 
{ 
    vec4 eyePos = modelview * position; 
    vec4 projVoxel = projection * vec4(spriteSize,spriteSize,eyePos.z,eyePos.w); 
    vec2 projSize = screenSize * projVoxel.xy/projVoxel.w; 
    gl_PointSize = 0.25 * (projSize.x+projSize.y); 
    gl_Position = projection * eyePos; 
} 

Điều này sẽ làm cho tất cả các sprites điểm có cùng kích thước không gian thế giới (và do đó kích thước không gian màn hình khác nhau tính bằng pixel).


Nhưng vân vân điểm trong khi vẫn được hỗ trợ hoàn toàn trong OpenGL hiện đại có nhược điểm. Một trong những nhược điểm lớn nhất là hành vi cắt xén của họ. Các điểm được cắt bớt ở tọa độ trung tâm của chúng (vì cắt được thực hiện trước khi rasterization và do đó trước khi điểm được "mở rộng"). Vì vậy, nếu trung tâm của điểm là bên ngoài của màn hình, phần còn lại của nó mà vẫn có thể tiếp cận vào khu vực xem không được hiển thị, do đó, tại tồi tệ nhất một khi điểm là nửa đường ra khỏi màn hình, nó sẽ đột nhiên biến mất. Tuy nhiên, điều này chỉ đáng chú ý (hoặc bất thường) nếu các điểm sprites quá lớn. Tuy nhiên, nếu chúng là những hạt rất nhỏ không bao phủ nhiều hơn một vài pixel, thì điều này sẽ không có vấn đề gì nhiều và tôi vẫn coi hệ thống hạt là trường hợp sử dụng chuẩn cho các điểm sprites, sử dụng chúng cho biển quảng cáo lớn. Nhưng nếu đây là một vấn đề, thì OpenGL hiện đại cung cấp nhiều cách khác để thực hiện sprites điểm, ngoài cách ngây thơ của tiền xây dựng tất cả các sprites như quads cá nhân trên CPU. Bạn vẫn có thể hiển thị chúng giống như một bộ đệm đầy đủ các điểm (và do đó theo cách chúng có khả năng thoát ra khỏi động cơ hạt dựa trên GPU của bạn). Để thực sự tạo ra các hình học quad sau đó, bạn có thể sử dụng các shader hình học, cho phép bạn tạo ra một quad từ một điểm duy nhất.Trước tiên, bạn chỉ thực hiện phép chuyển đổi modelview bên trong trình đổ bóng đỉnh:

uniform mat4 modelview; 

layout(location = 0) in vec4 position; 

void main() 
{ 
    gl_Position = modelview * position; 
} 

Sau đó, trình đổ bóng hình sẽ thực hiện phần còn lại của tác phẩm. Nó kết hợp các vị trí điểm với 4 góc của một [0,1] -quad chung và hoàn thành việc chuyển đổi sang kẹp-space:

const vec2 corners[4] = { 
    vec2(0.0, 1.0), vec2(0.0, 0.0), vec2(1.0, 1.0), vec2(1.0, 0.0) }; 

layout(points) in; 
layout(triangle_strip, max_vertices = 4) out; 

uniform mat4 projection; 
uniform float spriteSize; 

out vec2 texCoord; 

void main() 
{ 
    for(int i=0; i<4; ++i) 
    { 
     vec4 eyePos = gl_in[0].gl_Position;   //start with point position 
     eyePos.xy += spriteSize * (corners[i] - vec2(0.5)); //add corner position 
     gl_Position = projection * eyePos;    //complete transformation 
     texCoord = corners[i];       //use corner as texCoord 
     EmitVertex(); 
    } 
} 

Trong shader đoạn bạn sẽ thì tất nhiên sử dụng tùy chỉnh texCoord khác nhau thay vì của gl_PointCoord để tạo họa tiết vì chúng tôi không còn vẽ điểm thực tế nữa.


Hoặc một khả năng khác (và có thể nhanh hơn, vì tôi nhớ trình đổ bóng hình học có danh tiếng chậm) sẽ sử dụng hiển thị instanced. Bằng cách này, bạn có thêm VBO chứa các đỉnh của chỉ là một quad 2D chung (tức là [0,1] -square) và VBO cũ tốt của bạn chỉ chứa các vị trí điểm. Vậy thì bạn làm là vẽ quad đơn này nhiều lần (instanced), trong khi nguồn vị trí các trường hợp cá nhân từ quan điểm VBO:

glVertexAttribPointer(0, ...points...); 
glVertexAttribPointer(1, ...quad...); 
glVertexAttribDivisor(0, 1);   //advance only once per instance 
... 
glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, count); //draw #count quads 

Và trong vertex shader sau đó bạn lắp ráp các vị trí mỗi điểm với thực tế góc/quad-position (mà cũng là kết cấu phối hợp của đỉnh đó):

uniform mat4 modelview; 
uniform mat4 projection; 
uniform float spriteSize; 

layout(location = 0) in vec4 position; 
layout(location = 1) in vec2 corner; 

out vec2 texCoord; 

void main() 
{ 
    vec4 eyePos = modelview * position;   //transform to eye-space 
    eyePos.xy += spriteSize * (corner - vec2(0.5)); //add corner position 
    gl_Position = projection * eyePos;    //complete transformation 
    texCoord = corner; 
} 

này đạt được giống như các phương pháp hình học shader dựa, sprites điểm đúng-cắt với kích thước không gian thế giới phù hợp. Nếu bạn thực sự muốn bắt chước kích thước pixel không gian màn hình của các điểm ảnh thực tế, bạn cần phải đặt một số nỗ lực tính toán vào nó. Nhưng điều này được để lại như một bài tập và sẽ hoàn toàn trái ngược với sự chuyển đổi từ thế giới này sang màn hình khác từ trình đổ bóng sprite điểm.

+5

Wow! Thật là một câu trả lời! :-D – Joe

+0

Cảm ơn bạn rất nhiều vì sự giúp đỡ của bạn và giải thích tuyệt vời cho bạn của tôi :-) –

+0

Vì vậy, về cơ bản, khi tôi sử dụng 'glDrawArrays (GL_POINTS, ...)' Tôi render các điểm sprites? – BRabbit27

Các vấn đề liên quan