在 OpenGL 中,无论 zFar 有多大,对象都会消失在平截头体后面

问题描述 投票:0回答:0

我在 OpenGL 中绘制立方体。正交投影效果很好,但透视投影会出现黑屏。

所以我尝试了两种投影类型之间的插值,以了解透视失败的原因。
投影 = alpha*persp + (1-alpha)*ortho.
对于 alpha=0(纯正交),它工作正常,但在 alpha=.48 附近,立方体的远端开始被 zFar 处的墙剪裁。这会随着 alpha 的增加而继续,到 alpha = .508 时,整个立方体将被剪裁并且根本不会出现。

我预计增加平截头体的 zFar 会解决这个问题,但它根本没有影响。尽管增加了 zFar,立方体总是消失在 alpha=.48 和 alpha=.51 之间的后墙后面。

要看我说的效果就下载代码,编译,运行。它要求您输入 alpha。在 alpha=0 时它是纯正字法,在 alpha=1 时它是纯透视。但是从 alpha=.48 开始,立方体开始消失在裁剪空间的后墙后面,到 alpha=.51 时,整个立方体都被裁剪掉了。改变 zFar=frustum[5] 没有任何区别,这是这里的大惊喜。

这是完整的代码、着色器和所有内容。将其复制到文件 vertex.glsl、fragment.glsl 和 test.c 中。之后它可以被编译 gcc -g -o 测试 test.c

pkg-config --cflags --libs gtk+-3.0
-lepoxy

这里是着色器 vertex.glsl 和 fragment.glsl


/*----------------------------- vertex.glsl ------------------------------*/
#version 330 core

layout (location = 0) in vec3 point;
layout (location = 1) in vec3 colorIn;

uniform mat4 proj;

out vec3 colorOut;

void main (void)
{
  gl_Position = proj*vec4(point, 1.0);
  colorOut=colorIn;
}

/*------------------------------- fragment.glsl -----------------------------*/

#version 330 core

out vec4 FragColor;
in  vec3 colorOut;

void main(void)
{
  FragColor = vec4(colorOut, 1.0);
}

这是代码 test.c,尽可能简单。只有 388 行,其中大部分是坚如磐石的标准内容。谜团在于后墙剪裁不响应 zFar=frustum[5] 的任何变化。

//gcc -g -o test test.c `pkg-config --cflags --libs gtk+-3.0` -lepoxy

#include <epoxy/gl.h>
#include <gtk/gtk.h>


static void realize(GtkGLArea *gl_area);
static void render (GtkGLArea *gl_area);
static float *chessboard_vba(void);
static float *cube_vba(void);
static void interpolate_proj1_and_proj2(GLfloat *proj1, GLfloat *proj2, GLfloat alpha);
static int get_perspective_from_frustum(GLfloat *persp, GLfloat *frustum);
static int get_orthography_from_frustum(GLfloat *ortho, GLfloat *frustum);
static void print_vba(float *vba, int nvertex, int span);
static int simulate_vshader_and_gl_position(float *point, float *proj);

static void let_A_equal_Bmatrix_times_C_4c(float *A, float *B, float *C);
static void let_A_equal_Bmatrix_times_C_3c(float *A, float *B, float *C);
static void print_matrix_4c(float *M);

static GLuint shader_load_program(const char *vertex, const char *fragment);
static GLuint shader_load_file(const char *filename, GLenum type);
static void shader_print_log(GLuint object);

GLuint program;
GLuint vao, vbo;
GLint  uni_proj;
GtkGLArea *gl_area;
GLfloat frustum[] = {-0.01, 0.01, -0.009, 0.009, 0.02, 1000};
GLfloat proj[16], ortho[16], persp[16];

/*------------------------------------------------------------------------*/

int main(int argc, char *argv[])
{
  GtkWidget *window;
  GtkWidget *gl_area;
  
  gtk_init(&argc, &argv);
  
  // Initialize Window
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title(GTK_WINDOW(window), "Shader Test");
  gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
  gtk_window_set_default_size(GTK_WINDOW(window), 640, 640);
  gtk_window_set_default_size(GTK_WINDOW(window), 640, 480);
  gtk_window_set_type_hint(GTK_WINDOW(window), GDK_WINDOW_TYPE_HINT_UTILITY);
  g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);

  //Initialize GTK GL Area
  gl_area = gtk_gl_area_new();
  gtk_widget_set_vexpand(gl_area, TRUE);
  gtk_widget_set_hexpand(gl_area, TRUE);
  g_signal_connect(gl_area, "realize", G_CALLBACK(realize), NULL);
  g_signal_connect(gl_area, "render" , G_CALLBACK(render) , NULL);
  gtk_container_add(GTK_CONTAINER(window), gl_area);
  glClearColor(.5, .5, .5, 1);
  gtk_widget_set_can_focus(GTK_WIDGET(gl_area), TRUE);
  gtk_widget_grab_focus(GTK_WIDGET(gl_area));

  // Show widgets
  gtk_widget_show_all(window);
  gtk_main();
  
  return 0;
}

/*------------------------------------------------------------------------*/

static void realize(GtkGLArea *gl_area)
{
  int err;
  GLuint vao;
  GLfloat *vba, alpha;
  
  gtk_gl_area_make_current(gl_area);
  glClearColor(.5, .5, .5, 1);
  
  glGenVertexArrays(1, &vao);
  glBindVertexArray(vao);
  
  const char *vs = "vertex.glsl";
  const char *fs = "fragment.glsl";
  program = shader_load_program(vs, fs);
  glUseProgram(program);

  vba=cube_vba();//"vertex buffer array"
  
  //ortho or persp.  Maybe decide based on user input. 
  get_perspective_from_frustum(proj, frustum);
  get_orthography_from_frustum(ortho, frustum);
  printf("Enter alpha\n");
  scanf("%f", &alpha);
  interpolate_proj1_and_proj2(proj, ortho, alpha);

  //see if corner 5 really does go beyond z=1
  simulate_vshader_and_gl_position(vba+72, proj);

  uni_proj = glGetUniformLocation(program, "proj");
  glUniformMatrix4fv(uni_proj, 1, GL_FALSE, proj);
  
  while ((err = glGetError()))
    printf("WARNING: glError A errno=%i, err\n", err);
  
  
  glGenBuffers(1, &vbo);
  glBindBuffer(GL_ARRAY_BUFFER, vbo);
  glBufferData(GL_ARRAY_BUFFER, 2*8*8*6*sizeof(GLfloat), vba, GL_STATIC_DRAW);
  //glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);//might not be needed here
  
  glFlush();
  free(vba);
  
  while ((err = glGetError()))
    printf("WARNING: glError B errno=%i, err\n", err);
}

/*------------------------------------------------------------------------*/

static void render(GtkGLArea *gl_area)
{
  int i, j, err;
  int lseg=8, span=2*3, stride=6*sizeof(GLfloat);
  long rpointer=0, cpointer=3*sizeof(GLfloat);
  
  glBindBuffer(GL_ARRAY_BUFFER, vbo);
  glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, stride, (void *)rpointer);
  glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, stride, (void *)cpointer);
  
  glEnableVertexAttribArray(0);
  glEnableVertexAttribArray(1);
  glLineWidth(1);
  
  glDrawArrays(GL_LINES, 0, 2*12);
  while ((err = glGetError()))
    printf("WARNING: glError B errno=%i, err\n", err);
}

/*------------------------------------------------------------------------*/

static float *cube_vba()
{
  int i, j, k, l, m, ibase, ineigh[3], span=2*3, offset, ibw, dum;
  GLfloat *vba, size=.05, *r[2], color[]={0,.5,0}, corner[8][3];
  GLfloat  Re[] ={0.98039,  0.01960, -0.19607,
          0.01960,  0.98039,  0.19607,
          0.19607, -0.19607,  0.96078};//small rotation about x, y axes
  
  for (k=0; k<2; k++)
    for (j=0; j<2; j++)
      for (i=0; i<2; i++) {
    offset=4*k+2*j+i;
    corner[offset][0] = 2*(i-.5)*size;
    corner[offset][1] = 2*(j-.5)*size;
    corner[offset][2] = 2*(k-.5)*size + .04;//z has to be positive
    let_A_equal_Bmatrix_times_C_3c(corner[offset], Re, corner[offset]);//so you can see all corners in ortho mode
      }
  
  vba=(GLfloat *)malloc(2*12*span*sizeof(GLfloat));
  memset(vba, 0, 2*12*span*sizeof(GLfloat));
  
  for (m=ibase=0; m<4; m++) {
    //Indices of base corners. Neighbors of corners 0,3,5,6 cover all sides
    i=(m==1||m==2);  j=(m==1||m==3);  k=(m==2||m==3);
    ibase     = 4* k + 2* j +  i;
    ineigh[2] = 4*!k + 2* j +  i; //
    ineigh[1] = 4* k + 2*!j +  i; //neighboring corners, ie, one different index
    ineigh[0] = 4* k + 2* j + !i; //
    for (l=0; l<3; l++) {
      offset=3*m*2*span + l*2*span;
      r[0] = corner[ibase ];
      r[1] = corner[ineigh[l]];
      memcpy(vba+offset  , r[0]  , 3*sizeof(GLfloat));
      memcpy(vba+offset+3, color , 3*sizeof(GLfloat));
      memcpy(vba+offset+6, r[1]  , 3*sizeof(GLfloat));
      memcpy(vba+offset+9, color , 3*sizeof(GLfloat));
      dum=1;
    }
    dum=1;
  }

  //print_vba(vba, 12, 6);

  return vba;
}

/*------------------------------------------------------------------------*/

static float *chessboard_vba()
{
  int i, j, offset, ibw, *iba, dum;
  GLfloat *vba, size=.1, r[2][3], z=1, bw[2][3]={{0,0,0},{1,1,1}};
  
  vba=(GLfloat *)malloc(2*8*8*6*sizeof(GLfloat));
  
  for (j=0; j<8; j++) {
    ibw=j%2;
    for (i=0; i<8; i++) {
      ibw=!ibw;
      offset=2*(8*j+i)*2*3;
      r[0][0]=(i  -4)*size;   r[0][1]=(j-4)*size;  r[0][2]=z;
      r[1][0]=(i+1-4)*size;   r[1][1]=(j-4)*size;  r[1][2]=z;
      memcpy(vba+offset  , r[0]   , 3*sizeof(GLfloat));
      memcpy(vba+offset+3, bw+ibw , 3*sizeof(GLfloat));
      memcpy(vba+offset+6 , r[1]  , 3*sizeof(GLfloat));
      memcpy(vba+offset+9, bw+ibw , 3*sizeof(GLfloat));
      dum=1;
    }
  }
  
  return vba;
}

/*------------------------------------------------------------------------*/

static void interpolate_proj1_and_proj2(GLfloat *proj1, GLfloat *proj2, GLfloat alpha)
{
  int i, dum;

  for (i=0; i<16; i++)
    {proj[i] = alpha*proj1[i] + (1-alpha)*proj2[i];}

  dum=1;
}

/*------------------------------------------------------------------------*/
//letters stand for:  left, right, bottom, top, near, far, width, height, depth
static int get_perspective_from_frustum(GLfloat *proj, GLfloat *frustum)
{
  float l=frustum[0], r=frustum[1], b=frustum[2], t=frustum[3], n=frustum[4];
  float f=frustum[5], w=r-l, h=t-b, d=f-n;
  
  memset(proj, 0, 16*sizeof(float));
  
  proj[ 0]= 2*n/w;  proj[ 1]= 0    ;  proj[ 2]= (r+l)/w;  proj[ 3]= 0      ;
  proj[ 4]= 0    ;  proj[ 5]= 2*n/h;  proj[ 6]= (t+b)/h;  proj[ 7]= 0      ;
  proj[ 8]= 0    ;  proj[ 9]= 0    ;  proj[10]=-(f+n)/d;  proj[11]=-2*f*n/d;
  proj[12]= 0    ;  proj[13]= 0    ;  proj[14]= -1     ;  proj[15]= 0      ;
  
  return 1;
}

/*------------------------------------------------------------------------*/
//letters stand for w=width, h=height, d=depth
static int get_orthography_from_frustum(GLfloat *ortho, GLfloat *frustum)
{
  float w=frustum[1]-frustum[0], h=frustum[3]-frustum[2], d=frustum[5]-frustum[4];
  
  memset(ortho, 0, 16*sizeof(GLfloat));
  ortho[ 0] = .2/w;
  ortho[ 5] = .2/h;
  ortho[10] = .2/d;
  ortho[15] = 1;
  
  return 1;
}


/*------------------------------------------------------------------------*/

static void print_vba(float *vba, int Nvertex, int span)
{
  int i, j, dum;
  
  for (j=0; j<Nvertex; j++) {
    for (i=0; i<2*span; i++)
      printf("%6.3f ", vba[2*j*span+i]);
    printf("\n");
    dum=1;
  }
}

/*------------------------------------------------------------------------*/
/*------------------------------------------------------------------------*/

static int simulate_vshader_and_gl_position(float *point, float *proj)
{
  int i, j;
  float point4[]={point[0], point[1], point[2], 1}, p_point[]={0,0,0,0}, view_point[4];
  
  //This gives the output of the vertex shader to the GPU
  let_A_equal_Bmatrix_times_C_4c(p_point,  proj, point4);
  
  //This is where gl will put the point in the gl_area (?)
  for (i=0; i<3; i++)
    view_point[i] = p_point[i]/p_point[2];
  
  return 1;
}

/*------------------------------------------------------------------------*/

static void print_matrix_4c(float *M)
{
  int i, j;
  if (!M) return;
  
  for (j=0; j<4; j++) {
    for (i=0; i<4; i++)
      printf("%12.8f ", M[4*j+i]);
    printf("\n");
  }
}

/*------------------------------------------------------------------------*/

static void let_A_equal_Bmatrix_times_C_4c(float *A, float *B, float *C)
{
  float Ct[4];

  memcpy(Ct, C, 4*sizeof(float));
  A[0] = B[ 0]*Ct[0] + B[ 1]*Ct[1] + B[ 2]*Ct[2] + B[ 3]*Ct[3];
  A[1] = B[ 4]*Ct[0] + B[ 5]*Ct[1] + B[ 6]*Ct[2] + B[ 7]*Ct[3];
  A[2] = B[ 8]*Ct[0] + B[ 9]*Ct[1] + B[10]*Ct[2] + B[11]*Ct[3];
  A[3] = B[12]*Ct[0] + B[13]*Ct[1] + B[14]*Ct[2] + B[15]*Ct[3];
}

/*------------------------------------------------------------------------*/

static void let_A_equal_Bmatrix_times_C_3c(float *A, float *B, float *C)
{
  float Ct[3];

  memcpy(Ct, C, 3*sizeof(float));
  A[0] = B[0]*Ct[0] + B[1]*Ct[1] + B[2]*Ct[2];
  A[1] = B[3]*Ct[0] + B[4]*Ct[1] + B[5]*Ct[2];
  A[2] = B[6]*Ct[0] + B[7]*Ct[1] + B[8]*Ct[2];
}

/*------------------------------------------------------------------------*/
/*------------------------------------------------------------------------*/
/*------------------------------------------------------------------------*/

static GLuint shader_load_program(const char *vertex, const char *fragment)
{
  GLint link_ok;
  GLuint program;
  
  GLuint vs = shader_load_file(vertex, GL_VERTEX_SHADER);
  GLuint fs = shader_load_file(fragment, GL_FRAGMENT_SHADER);
  
  if(vs == 0 || fs == 0) {
    return 0;
  }
  
  program = glCreateProgram();
  glAttachShader(program, vs);
  glAttachShader(program, fs);
  glLinkProgram(program);
  glGetProgramiv(program, GL_LINK_STATUS, &link_ok);
  
  glDetachShader(program, vs);
  glDetachShader(program, fs);
  
  return program;
}

/*------------------------------------------------------------------------*/

static GLuint shader_load_file(const char *filename, GLenum type)
{
  FILE *fp;
  unsigned int file_len;
  char *source;
  GLuint shader;
  GLint compile_ok;
  
  fp = fopen(filename, "r");
  if(fp == NULL) {
    fprintf(stderr, "Could not open %s for reading\n", filename);
    return 0;
  }
  
  fseek(fp, 0, SEEK_END);
  file_len = ftell(fp);
  fseek(fp, 0, SEEK_SET);
  
  source = malloc(file_len + 1);
  fread(source, file_len, sizeof(char), fp);
  fclose(fp);
  source[file_len] = '\0';
  
  const GLchar *sources[] = { source };
  
  shader = glCreateShader(type);
  glShaderSource(shader, 1, sources, NULL);
  glCompileShader(shader);
  
  free(source);
  
  glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_ok);
  
  return shader;
}

opengl perspective clipping frustum
© www.soinside.com 2019 - 2024. All rights reserved.