int FLightBuffer::UploadLights(FDynLightData &data) { int size0 = data.arrays[0].Size()/4; int size1 = data.arrays[1].Size()/4; int size2 = data.arrays[2].Size()/4; int totalsize = size0 + size1 + size2 + 1; // pointless type casting because some compilers can't print enough warnings. if (mBlockAlign > 0 && (unsigned int)totalsize + (mIndex % mBlockAlign) > mBlockSize) { mIndex = ((mIndex + mBlockAlign) / mBlockAlign) * mBlockAlign; // can't be rendered all at once. if ((unsigned int)totalsize > mBlockSize) { int diff = totalsize - (int)mBlockSize; size2 -= diff; if (size2 < 0) { size1 += size2; size2 = 0; } if (size1 < 0) { size0 += size1; size1 = 0; } totalsize = size0 + size1 + size2 + 1; } } if (totalsize <= 1) return -1; if (mIndex + totalsize > mBufferSize/4) { // reallocate the buffer with twice the size unsigned int newbuffer; // first unmap the old buffer glBindBuffer(mBufferType, mBufferId); glUnmapBuffer(mBufferType); // create and bind the new buffer, bind the old one to a copy target (too bad that DSA is not yet supported well enough to omit this crap.) glGenBuffers(1, &newbuffer); glBindBufferBase(mBufferType, LIGHTBUF_BINDINGPOINT, newbuffer); glBindBuffer(mBufferType, newbuffer); // Note: Some older AMD drivers don't do that in glBindBufferBase, as they should. glBindBuffer(GL_COPY_READ_BUFFER, mBufferId); // create the new buffer's storage (twice as large as the old one) mBufferSize *= 2; mByteSize *= 2; if (gl.lightmethod == LM_DIRECT) { glBufferStorage(mBufferType, mByteSize, NULL, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT); mBufferPointer = (float*)glMapBufferRange(mBufferType, 0, mByteSize, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT); } else { glBufferData(mBufferType, mByteSize, NULL, GL_DYNAMIC_DRAW); mBufferPointer = (float*)glMapBufferRange(mBufferType, 0, mByteSize, GL_MAP_WRITE_BIT|GL_MAP_INVALIDATE_BUFFER_BIT); } // copy contents and delete the old buffer. glCopyBufferSubData(GL_COPY_READ_BUFFER, mBufferType, 0, 0, mByteSize/2); glBindBuffer(GL_COPY_READ_BUFFER, 0); glDeleteBuffers(1, &mBufferId); mBufferId = newbuffer; } float *copyptr; assert(mBufferPointer != NULL); if (mBufferPointer == NULL) return -1; copyptr = mBufferPointer + mIndex * 4; float parmcnt[] = { 0, float(size0), float(size0 + size1), float(size0 + size1 + size2) }; memcpy(©ptr[0], parmcnt, 4 * sizeof(float)); memcpy(©ptr[4], &data.arrays[0][0], 4 * size0*sizeof(float)); memcpy(©ptr[4 + 4*size0], &data.arrays[1][0], 4 * size1*sizeof(float)); memcpy(©ptr[4 + 4*(size0 + size1)], &data.arrays[2][0], 4 * size2*sizeof(float)); unsigned int bufferindex = mIndex; mIndex += totalsize; draw_dlight += (totalsize-1) / 2; return bufferindex; }
void render_text(wchar_t * str, float x, float y, float size) { float scale = size / GLYPH_DIM; int len = wcslen(str); int i; int buf_index = 0; float cur_x = 0; float cur_y = 0; float *buf; if(vbo_index + len > VBO_MAX_SIZE) return; glBindBuffer(GL_ARRAY_BUFFER, font_vbo); buf = glMapBufferRange(GL_ARRAY_BUFFER, vbo_index*glyph_data_size, (vbo_index+len)*glyph_data_size, GL_MAP_WRITE_BIT); for(i = 0; i < len; i++) { float *vertices = buf + buf_index * VERTEX_DATA_SIZE; int cc = str[i]; if(cc > 256 || cc < 0) continue; // well we don't know this char int cc_w = droid.width[cc]; // current char width if(cc == '\n') { cur_x = 0; cur_y += GLYPH_DIM*scale; continue; } /* pos */ vertices[0] = x+cur_x; vertices[1] = y+cur_y; /* uv */ vertices[2] = glyph_data[cc][2]; vertices[3] = glyph_data[cc][3]; /* color */ memcpy(&vertices[4], color, 4*sizeof(float)); /* pos */ vertices[8] = x+cur_x; vertices[9] = y+cur_y + GLYPH_DIM*scale; /* uv */ vertices[10] = glyph_data[cc][6]; vertices[11] = glyph_data[cc][7]; /* color */ memcpy(&vertices[12], color, 4*sizeof(float)); /* pos */ vertices[16] = x+cur_x + cc_w*scale; vertices[17] = y+cur_y + GLYPH_DIM*scale; /* uv */ vertices[18] = glyph_data[cc][10]; vertices[19] = glyph_data[cc][11]; /* color */ memcpy(&vertices[20], color, 4*sizeof(float)); vertices[24] = x+cur_x + cc_w*scale; vertices[25] = y+cur_y; vertices[26] = glyph_data[cc][14]; vertices[27] = glyph_data[cc][15]; memcpy(&vertices[28], color, 4*sizeof(float)); cur_x += cc_w*scale; buf_index++; // how many data wrote } glUnmapBuffer(GL_ARRAY_BUFFER); vbo_index+=buf_index; }
bool render() { glm::vec2 WindowSize(this->getWindowSize()); { glBindBuffer(GL_UNIFORM_BUFFER, BufferName[buffer::TRANSFORM]); glm::mat4* Pointer = (glm::mat4*)glMapBufferRange( GL_UNIFORM_BUFFER, 0, sizeof(glm::mat4), GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT); glm::mat4 Projection = glm::perspectiveFov(glm::pi<float>() * 0.25f, WindowSize.x, WindowSize.y, 0.1f, 100.0f); glm::mat4 Model = glm::mat4(1.0f); *Pointer = Projection * this->view() * Model; // Make sure the uniform buffer is uploaded glUnmapBuffer(GL_UNIFORM_BUFFER); } glViewportIndexedf(0, 0, 0, WindowSize.x, WindowSize.y); glClearBufferfv(GL_COLOR, 0, &glm::vec4(1.0f)[0]); glBindProgramPipeline(PipelineName); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D_ARRAY, TextureName); glBindVertexArray(VertexArrayName); glBindBufferBase(GL_UNIFORM_BUFFER, semantic::uniform::TRANSFORM0, BufferName[buffer::TRANSFORM]); glBeginQuery(GL_VERTICES_SUBMITTED_ARB, this->QueryName[statistics::VERTICES_SUBMITTED]); glBeginQuery(GL_PRIMITIVES_SUBMITTED_ARB, this->QueryName[statistics::PRIMITIVES_SUBMITTED]); glBeginQuery(GL_VERTEX_SHADER_INVOCATIONS_ARB, this->QueryName[statistics::VERTEX_SHADER_INVOCATIONS]); glBeginQuery(GL_TESS_CONTROL_SHADER_PATCHES_ARB, this->QueryName[statistics::TESS_CONTROL_SHADER_PATCHES]); glBeginQuery(GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB, this->QueryName[statistics::TESS_EVALUATION_SHADER_INVOCATIONS]); glBeginQuery(GL_GEOMETRY_SHADER_INVOCATIONS, this->QueryName[statistics::GEOMETRY_SHADER_INVOCATIONS]); glBeginQuery(GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB, this->QueryName[statistics::GEOMETRY_SHADER_PRIMITIVES_EMITTED]); glBeginQuery(GL_FRAGMENT_SHADER_INVOCATIONS_ARB, this->QueryName[statistics::FRAGMENT_SHADER_INVOCATIONS]); glBeginQuery(GL_COMPUTE_SHADER_INVOCATIONS_ARB, this->QueryName[statistics::COMPUTE_SHADER_INVOCATIONS]); glBeginQuery(GL_CLIPPING_INPUT_PRIMITIVES_ARB, this->QueryName[statistics::CLIPPING_INPUT_PRIMITIVES]); glBeginQuery(GL_CLIPPING_OUTPUT_PRIMITIVES_ARB, this->QueryName[statistics::CLIPPING_OUTPUT_PRIMITIVES]); glDrawElementsInstancedBaseVertexBaseInstance(GL_TRIANGLES, ElementCount, GL_UNSIGNED_SHORT, 0, 1, 0, 0); glEndQuery(GL_VERTICES_SUBMITTED_ARB); glEndQuery(GL_PRIMITIVES_SUBMITTED_ARB); glEndQuery(GL_VERTEX_SHADER_INVOCATIONS_ARB); glEndQuery(GL_TESS_CONTROL_SHADER_PATCHES_ARB); glEndQuery(GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB); glEndQuery(GL_GEOMETRY_SHADER_INVOCATIONS); glEndQuery(GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB); glEndQuery(GL_FRAGMENT_SHADER_INVOCATIONS_ARB); glEndQuery(GL_COMPUTE_SHADER_INVOCATIONS_ARB); glEndQuery(GL_CLIPPING_INPUT_PRIMITIVES_ARB); glEndQuery(GL_CLIPPING_OUTPUT_PRIMITIVES_ARB); std::array<GLuint, statistics::MAX> QueryResult; for(std::size_t i = 0; i < QueryResult.size(); ++i) glGetQueryObjectuiv(this->QueryName[i], GL_QUERY_RESULT, &QueryResult[i]); fprintf(stdout, "Verts: %d; Prims: (%d, %d); Shaders(%d, %d, %d, %d, %d, %d); Clip(%d, %d)\r", QueryResult[statistics::VERTICES_SUBMITTED], QueryResult[statistics::PRIMITIVES_SUBMITTED], QueryResult[statistics::GEOMETRY_SHADER_PRIMITIVES_EMITTED], QueryResult[statistics::VERTEX_SHADER_INVOCATIONS], QueryResult[statistics::TESS_CONTROL_SHADER_PATCHES], QueryResult[statistics::TESS_EVALUATION_SHADER_INVOCATIONS], QueryResult[statistics::GEOMETRY_SHADER_INVOCATIONS], QueryResult[statistics::FRAGMENT_SHADER_INVOCATIONS], QueryResult[statistics::COMPUTE_SHADER_INVOCATIONS], QueryResult[statistics::CLIPPING_INPUT_PRIMITIVES], QueryResult[statistics::CLIPPING_OUTPUT_PRIMITIVES]); return true; }
void *GPUBuffer::map(unsigned byte_size, unsigned access /*= GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT*/) { KS_ASSERT(byte_size <= m_size); bind(); return glMapBufferRange(m_target, 0, byte_size, access); }
void RefreshScreen(const void *data, unsigned width, unsigned height, size_t pitch) { //Draw a textured quad with graphics gTexture.width = width; gTexture.height = height; const uint32_t dataSize = width*height * 4; glBindBuffer(GL_PIXEL_UNPACK_BUFFER, gPBO); glBufferData(GL_PIXEL_UNPACK_BUFFER, dataSize, NULL, GL_DYNAMIC_DRAW); GLubyte* ptr = (GLubyte*)glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, dataSize, GL_MAP_WRITE_BIT); if (ptr == NULL) return; uint32_t* dst = (uint32_t*)ptr; memcpy(dst, data, dataSize); glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); // release the mapped buffer glBindTexture(GL_TEXTURE_2D, gTexture.glName); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, 0); glActiveTexture(GL_TEXTURE0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); // Set clamping modes glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); //glBindFramebuffer(GL_DRAW_FRAMEBUFFER, gFBO); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); // Render to the screen // Render on the whole framebuffer, complete from the lower left corner to the upper right //Draw on screen glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glViewport(0,0,640,480); // Clear the screen glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Use our shader glUseProgram(gProgramId); // Bind our texture in Texture Unit 0 glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, gTexture.glName); // Set our "renderedTexture" sampler to user Texture Unit 0 glUniform1i(gTexID, 0); //Draw the vertices glEnableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER, gQuad_vertexbuffer); glVertexAttribPointer( 0, // attribute 0. No particular reason for 0, but must match the layout in the shader. 3, // size GL_FLOAT, // type GL_FALSE, // normalized? 0, // stride (void*)0 // array buffer offset ); // Draw the triangles ! glDrawArrays(GL_TRIANGLES, 0, 6); // 2*3 indices starting at 0 -> 2 triangles glDisableVertexAttribArray(0); }
void startup(void) { int i, j; load_shaders(); vmath::vec4 * initial_positions = new vmath::vec4 [POINTS_TOTAL]; vmath::vec3 * initial_velocities = new vmath::vec3 [POINTS_TOTAL]; vmath::ivec4 * connection_vectors = new vmath::ivec4 [POINTS_TOTAL]; int n = 0; for (j = 0; j < POINTS_Y; j++) { float fj = (float)j / (float)POINTS_Y; for (i = 0; i < POINTS_X; i++) { float fi = (float)i / (float)POINTS_X; initial_positions[n] = vmath::vec4((fi - 0.5f) * (float)POINTS_X, (fj - 0.5f) * (float)POINTS_Y, 0.6f * sinf(fi) * cosf(fj), 1.0f); initial_velocities[n] = vmath::vec3(0.0f); connection_vectors[n] = vmath::ivec4(-1); if (j != (POINTS_Y - 1)) { if (i != 0) connection_vectors[n][0] = n - 1; if (j != 0) connection_vectors[n][1] = n - POINTS_X; if (i != (POINTS_X - 1)) connection_vectors[n][2] = n + 1; if (j != (POINTS_Y - 1)) connection_vectors[n][3] = n + POINTS_X; } n++; } } glGenVertexArrays(2, m_vao); glGenBuffers(5, m_vbo); for (i = 0; i < 2; i++) { glBindVertexArray(m_vao[i]); glBindBuffer(GL_ARRAY_BUFFER, m_vbo[POSITION_A + i]); glBufferData(GL_ARRAY_BUFFER, POINTS_TOTAL * sizeof(vmath::vec4), initial_positions, GL_DYNAMIC_COPY); glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, NULL); glEnableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER, m_vbo[VELOCITY_A + i]); glBufferData(GL_ARRAY_BUFFER, POINTS_TOTAL * sizeof(vmath::vec3), initial_velocities, GL_DYNAMIC_COPY); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, NULL); glEnableVertexAttribArray(1); glBindBuffer(GL_ARRAY_BUFFER, m_vbo[CONNECTION]); glBufferData(GL_ARRAY_BUFFER, POINTS_TOTAL * sizeof(vmath::ivec4), connection_vectors, GL_STATIC_DRAW); glVertexAttribIPointer(2, 4, GL_INT, 0, NULL); glEnableVertexAttribArray(2); } delete [] connection_vectors; delete [] initial_velocities; delete [] initial_positions; glGenTextures(2, m_pos_tbo); glBindTexture(GL_TEXTURE_BUFFER, m_pos_tbo[0]); glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, m_vbo[POSITION_A]); glBindTexture(GL_TEXTURE_BUFFER, m_pos_tbo[1]); glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, m_vbo[POSITION_B]); int lines = (POINTS_X - 1) * POINTS_Y + (POINTS_Y - 1) * POINTS_X; glGenBuffers(1, &m_index_buffer); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_index_buffer); glBufferData(GL_ELEMENT_ARRAY_BUFFER, lines * 2 * sizeof(int), NULL, GL_STATIC_DRAW); int * e = (int *)glMapBufferRange(GL_ELEMENT_ARRAY_BUFFER, 0, lines * 2 * sizeof(int), GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT); for (j = 0; j < POINTS_Y; j++) { for (i = 0; i < POINTS_X - 1; i++) { *e++ = i + j * POINTS_X; *e++ = 1 + i + j * POINTS_X; } } for (i = 0; i < POINTS_X; i++) { for (j = 0; j < POINTS_Y - 1; j++) { *e++ = i + j * POINTS_X; *e++ = POINTS_X + i + j * POINTS_X; } } glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); }
void raytracer_app::render(double currentTime) { static const GLfloat zeros[] = { 0.0f, 0.0f, 0.0f, 0.0f }; static const GLfloat gray[] = { 0.1f, 0.1f, 0.1f, 0.0f }; static const GLfloat ones[] = { 1.0f }; static double last_time = 0.0; static double total_time = 0.0; if (!paused) total_time += (currentTime - last_time); last_time = currentTime; float f = (float)total_time; vmath::vec3 view_position = vmath::vec3(sinf(f * 0.3234f) * 28.0f, cosf(f * 0.4234f) * 28.0f, cosf(f * 0.1234f) * 28.0f); // sinf(f * 0.2341f) * 20.0f - 8.0f); vmath::vec3 lookat_point = vmath::vec3(sinf(f * 0.214f) * 8.0f, cosf(f * 0.153f) * 8.0f, sinf(f * 0.734f) * 8.0f); vmath::mat4 view_matrix = vmath::lookat(view_position, lookat_point, vmath::vec3(0.0f, 1.0f, 0.0f)); glBindBufferBase(GL_UNIFORM_BUFFER, 0, uniforms_buffer); uniforms_block * block = (uniforms_block *)glMapBufferRange(GL_UNIFORM_BUFFER, 0, sizeof(uniforms_block), GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT); vmath::mat4 model_matrix = vmath::scale(7.0f); // f = 0.0f; block->mv_matrix = view_matrix * model_matrix; block->view_matrix = view_matrix; block->proj_matrix = vmath::perspective(50.0f, (float)info.windowWidth / (float)info.windowHeight, 0.1f, 1000.0f); glUnmapBuffer(GL_UNIFORM_BUFFER); glBindBufferBase(GL_UNIFORM_BUFFER, 1, sphere_buffer); sphere * s = (sphere *)glMapBufferRange(GL_UNIFORM_BUFFER, 0, 128 * sizeof(sphere), GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT); int i; for (i = 0; i < 128; i++) { // float f = 0.0f; float fi = (float)i / 128.0f; s[i].center = vmath::vec3(sinf(fi * 123.0f + f) * 15.75f, cosf(fi * 456.0f + f) * 15.75f, (sinf(fi * 300.0f + f) * cosf(fi * 200.0f + f)) * 20.0f); s[i].radius = fi * 2.3f + 3.5f; float r = fi * 61.0f; float g = r + 0.25f; float b = g + 0.25f; r = (r - floorf(r)) * 0.8f + 0.2f; g = (g - floorf(g)) * 0.8f + 0.2f; b = (b - floorf(b)) * 0.8f + 0.2f; s[i].color = vmath::vec4(r, g, b, 1.0f); } glUnmapBuffer(GL_UNIFORM_BUFFER); glBindBufferBase(GL_UNIFORM_BUFFER, 2, plane_buffer); plane * p = (plane *)glMapBufferRange(GL_UNIFORM_BUFFER, 0, 128 * sizeof(plane), GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT); //for (i = 0; i < 1; i++) { p[0].normal = vmath::vec3(0.0f, 0.0f, -1.0f); p[0].d = 30.0f; p[1].normal = vmath::vec3(0.0f, 0.0f, 1.0f); p[1].d = 30.0f; p[2].normal = vmath::vec3(-1.0f, 0.0f, 0.0f); p[2].d = 30.0f; p[3].normal = vmath::vec3(1.0f, 0.0f, 0.0f); p[3].d = 30.0f; p[4].normal = vmath::vec3(0.0f, -1.0f, 0.0f); p[4].d = 30.0f; p[5].normal = vmath::vec3(0.0f, 1.0f, 0.0f); p[5].d = 30.0f; } glUnmapBuffer(GL_UNIFORM_BUFFER); glBindBufferBase(GL_UNIFORM_BUFFER, 3, light_buffer); light * l = (light *)glMapBufferRange(GL_UNIFORM_BUFFER, 0, 128 * sizeof(light), GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT); f *= 1.0f; for (i = 0; i < 128; i++) { float fi = 3.33f - (float)i; // / 35.0f; l[i].position = vmath::vec3(sinf(fi * 2.0f - f) * 15.75f, cosf(fi * 5.0f - f) * 5.75f, (sinf(fi * 3.0f - f) * cosf(fi * 2.5f - f)) * 19.4f); } glUnmapBuffer(GL_UNIFORM_BUFFER); glBindVertexArray(vao); glViewport(0, 0, info.windowWidth, info.windowHeight); glUseProgram(prepare_program); glUniformMatrix4fv(uniforms.ray_lookat, 1, GL_FALSE, view_matrix); glUniform3fv(uniforms.ray_origin, 1, view_position); glUniform1f(uniforms.aspect, (float)info.windowHeight / (float)info.windowWidth); glBindFramebuffer(GL_FRAMEBUFFER, ray_fbo[0]); static const GLenum draw_buffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3, GL_COLOR_ATTACHMENT4, GL_COLOR_ATTACHMENT5 }; glDrawBuffers(6, draw_buffers); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glUseProgram(trace_program); recurse(0); glUseProgram(blit_program); glBindFramebuffer(GL_FRAMEBUFFER, 0); glDrawBuffer(GL_BACK); glActiveTexture(GL_TEXTURE0); switch (debug_mode) { case DEBUG_NONE: glBindTexture(GL_TEXTURE_2D, tex_composite); break; case DEBUG_REFLECTED: glBindTexture(GL_TEXTURE_2D, tex_reflected[debug_depth]); break; case DEBUG_REFRACTED: glBindTexture(GL_TEXTURE_2D, tex_refracted[debug_depth]); break; case DEBUG_REFLECTED_COLOR: glBindTexture(GL_TEXTURE_2D, tex_reflection_intensity[debug_depth]); break; case DEBUG_REFRACTED_COLOR: glBindTexture(GL_TEXTURE_2D, tex_refraction_intensity[debug_depth]); break; } glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); /* glClearBufferfv(GL_COLOR, 0, gray); glClearBufferfv(GL_DEPTH, 0, ones); glBindVertexArray(vao); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); */ }