Recently I've been writing a game engine in OpenGL and have a question. When I generate my BSP file (Quake 3), it generates very odd vertices!! I've checked the vertex vector, heres a screenshot of what it contains (it repeats itself)
I've checked glGetError, and it returns nothing (zero)! I'm really stumped here, and I have no idea what to do.
Here is my code:
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/vec3.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <vector>
#include "map.h"
#include <fstream>
#include <memory>
#include "game_manager.h"
std::vector<BSPVerts> vertexes;
bool KikoBSP::load_map(std::string file_name)
{
this->file.open(file_name.c_str(), std::ios::in | std::ios::binary);
if (this->file.is_open())
{
this->file.read(reinterpret_cast<char*>(&this->header), sizeof(this->header));
BSPEntities* ents = new BSPEntities;
ents->ents_array = new char[this->header.lumps[BSPLUMPS::ENTITIES].length];
this->num_textures = this->header.lumps[BSPLUMPS::TEXTURES].length / sizeof(BSPTexture);
this->num_planes = this->header.lumps[BSPLUMPS::PLANES].length / sizeof(BSPPlane);
this->num_textures = this->header.lumps[BSPLUMPS::TEXTURES].length / sizeof(BSPTexture);
this->num_nodes = this->header.lumps[BSPLUMPS::NODES].length / sizeof(BSPNode);
this->num_leafs = this->header.lumps[BSPLUMPS::LEAFS].length / sizeof(BSPLeaf);
this->num_leaf_faces = this->header.lumps[BSPLUMPS::LEAF_FACES].length / sizeof(BSPLeafFace);
this->num_leaf_brushes = this->header.lumps[BSPLUMPS::LEAF_BRUSHES].length / sizeof(BSPLeafBrush);
this->num_models = this->header.lumps[BSPLUMPS::MODELS].length / sizeof(BSPModel);
this->num_brushes = this->header.lumps[BSPLUMPS::BRUSHES].length / sizeof(BSPBrush);
this->num_brush_sides = this->header.lumps[BSPLUMPS::BRUSHSIDES].length / sizeof(BSPBrushSides);
this->num_vertexes = this->header.lumps[BSPLUMPS::VERTEXES].length / sizeof(BSPVerts);
this->num_meshverts = this->header.lumps[BSPLUMPS::MESHVERTS].length / sizeof(BSPMeshVerts);
this->num_effects = this->header.lumps[BSPLUMPS::EFFECTS].length / sizeof(BSPEffects);
this->num_faces = this->header.lumps[BSPLUMPS::FACES].length / sizeof(BSPFaces);
for (int32_t x = 0; x < this->num_vertexes; x++)
{
vertexes.push_back(*reinterpret_cast<BSPVerts*>(&x));
}
for (int32_t x = 0; x < this->num_textures; x++)
{
this->textures.push_back(*reinterpret_cast<BSPTexture*>(&x));
}
for (int32_t x = 0; x < this->num_meshverts; x++)
{
this->mesh_verts.push_back(*reinterpret_cast<BSPMeshVerts*>(&x));
}
for (int32_t x = 0; x < this->num_planes; x++)
{
this->planes.push_back(*reinterpret_cast<BSPPlane*>(&x));
}
for (int32_t x = 0; x < this->num_nodes; x++)
{
this->nodes.push_back(*reinterpret_cast<BSPNode*>(&x));
}
for (int32_t x = 0; x < this->num_leafs; x++)
{
this->leaf.push_back(*reinterpret_cast<BSPLeaf*>(&x));
}
for (int32_t x = 0; x < this->num_faces; x++)
{
this->faces.push_back(*reinterpret_cast<BSPFaces*>(&x));
}
for (int32_t x = 0; x < this->num_leaf_faces; x++)
{
this->leaf_faces.push_back(*reinterpret_cast<BSPLeafFace*>(&x));
}
for (int32_t x = 0; x < this->num_leaf_brushes; x++)
{
this->leaf_brush.push_back(*reinterpret_cast<BSPLeafBrush*>(&x));
}
for (int32_t x = 0; x < this->num_models; x++)
{
this->model.push_back(*reinterpret_cast<BSPModel*>(&x));
}
for (int32_t x = 0; x < this->num_brushes; x++)
{
this->brushes.push_back(*reinterpret_cast<BSPBrush*>(&x));
}
for (int32_t x = 0; x < this->num_brush_sides; x++)
{
this->brush_sides.push_back(*reinterpret_cast<BSPBrushSides*>(&x));
}
for (int32_t x = 0; x < this->num_effects; x++)
{
this->effect.push_back(*reinterpret_cast<BSPEffects*>(&x));
}
this->file.seekg(this->header.lumps[BSPLUMPS::ENTITIES].offset);
this->file.read(reinterpret_cast<char*>(ents->ents_array), this->header.lumps[BSPLUMPS::ENTITIES].length);
this->file.seekg(this->header.lumps[BSPLUMPS::TEXTURES].offset);
this->file.read(reinterpret_cast<char*>(textures.data()), this->header.lumps[BSPLUMPS::TEXTURES].length);
this->file.seekg(this->header.lumps[BSPLUMPS::PLANES].offset);
this->file.read(reinterpret_cast<char*>(this->planes.data()), this->header.lumps[BSPLUMPS::PLANES].length);
this->file.seekg(this->header.lumps[BSPLUMPS::NODES].offset);
this->file.read(reinterpret_cast<char*>(this->nodes.data()), this->header.lumps[BSPLUMPS::NODES].length);
this->file.seekg(this->header.lumps[BSPLUMPS::LEAFS].offset);
this->file.read(reinterpret_cast<char*>(this->leaf.data()), this->header.lumps[BSPLUMPS::LEAFS].length);
this->file.seekg(this->header.lumps[BSPLUMPS::LEAF_FACES].offset);
this->file.read(reinterpret_cast<char*>(this->leaf_faces.data()), this->header.lumps[BSPLUMPS::LEAF_FACES].length);
this->file.seekg(this->header.lumps[BSPLUMPS::LEAF_BRUSHES].offset);
this->file.read(reinterpret_cast<char*>(this->leaf_brush.data()), this->header.lumps[BSPLUMPS::LEAF_BRUSHES].length);
this->file.seekg(this->header.lumps[BSPLUMPS::MODELS].offset);
this->file.read(reinterpret_cast<char*>(this->model.data()), this->header.lumps[BSPLUMPS::MODELS].length);
this->file.seekg(this->header.lumps[BSPLUMPS::BRUSHES].offset);
this->file.read(reinterpret_cast<char*>(this->brushes.data()), this->header.lumps[BSPLUMPS::BRUSHES].length);
this->file.seekg(this->header.lumps[BSPLUMPS::BRUSHSIDES].offset);
this->file.read(reinterpret_cast<char*>(this->brush_sides.data()), this->header.lumps[BSPLUMPS::BRUSHSIDES].length);
this->file.seekg(this->header.lumps[BSPLUMPS::VERTEXES].offset);
for (int32_t x = 0; x < this->num_vertexes; x++)
{
this->file.read(reinterpret_cast<char*>(&vertexes.data()[x]), this->header.lumps[BSPLUMPS::VERTEXES].length);
}
this->file.seekg(this->header.lumps[BSPLUMPS::MESHVERTS].offset);
this->file.read(reinterpret_cast<char*>(this->mesh_verts.data()), this->header.lumps[BSPLUMPS::MESHVERTS].length);
this->file.seekg(this->header.lumps[BSPLUMPS::EFFECTS].offset);
this->file.read(reinterpret_cast<char*>(this->effect.data()), this->header.lumps[BSPLUMPS::EFFECTS].length);
this->file.seekg(this->header.lumps[BSPLUMPS::FACES].offset);
this->file.read(reinterpret_cast<char*>(this->faces.data()), this->header.lumps[BSPLUMPS::FACES].length);
std::printf("BSP VERSION: '%s'\n", this->header.magic);
if (std::strncmp(this->header.magic, "IBSP", 4) == 0)
{
std::printf("SUCCESS: VALID BSP FORMAT!\n");
}
else
{
std::printf("ERROR: INVALID BSP FORMAT!\n");
return false;
}
this->shader.load_shader("shaders/bsp.vs", "shaders/bsp.fs");
/* heres where I try to store the data for the faces vertices */
for (int32_t x = 0; x < this->num_faces; x++)
{
BSPFaces& face = this->faces[x];
for (int32_t vertices = 0; vertices < this->num_vertexes; vertices++)
{
BSPVerts* vert = reinterpret_cast<BSPVerts*>(&vertexes[x]);
this->vertices_vector.push_back(vert[x].position.x);
this->vertices_vector.push_back(vert[x].position.y);
this->vertices_vector.push_back(vert[x].position.z);
this->colors.push_back(vert[x].position.x / 0xFF);
this->colors.push_back(vert[x].position.y / 0xFF);
this->colors.push_back(vert[x].position.z / 0xFF);
this->indices.push_back(vert[x].position.x);
this->indices.push_back(vert[x].position.y);
this->indices.push_back(vert[x].position.z);
}
}
glGenVertexArrays(1, &this->vao);
glBindVertexArray(this->vao);
glGenBuffers(1, &this->vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, this->vertices_vector.size() * sizeof(float), &this->vertices_vector.front(), GL_STATIC_DRAW);
glGenBuffers(1, &this->color_vbo);
glBindBuffer(GL_ARRAY_BUFFER, this->color_vbo);
glBufferData(GL_ARRAY_BUFFER, this->colors.size() * sizeof(float), &this->colors.front(), GL_STATIC_DRAW);
this->coord3d = glGetAttribLocation(this->shader.program, "coord3d");
this->mvp = glGetUniformLocation(this->shader.program, "mvp");
this->attrib_color = glGetAttribLocation(this->shader.program, "v_color");
glBindBuffer(GL_ARRAY_BUFFER, this->vbo);
glVertexAttribPointer(this->coord3d, // attribute
3, // number of elements per vertex, here (R,G,B)
GL_FLOAT, // the currentBlock of each element
GL_FALSE, // take our values as-is
0, // no extra data between each position
nullptr // offset of first element
);
glBindBuffer(GL_ARRAY_BUFFER, this->color_vbo);
glVertexAttribPointer(this->attrib_color,
3,
GL_FLOAT,
GL_FALSE,
0,
nullptr
);
glBindVertexArray(0);
glVertexAttrib3fv(this->attrib_color, reinterpret_cast<float*>(this->colors.data()));
return true;
}
else
{
std::printf("ERROR: COULDN'T OPEN FILE!\n");
return false;
}
return false;
}
void KikoBSP::render(glm::vec3 position)
{
glBindVertexArray(this->vao);
glEnableVertexAttribArray(this->coord3d);
glEnableVertexAttribArray(this->attrib_color);
glm::mat4 model = glm::translate(glm::mat4(1.0), glm::vec3(position.x, position.y, position.z));
glm::mat4 mvp = game_manager->projection * game_manager->view * model;
glUniformMatrix4fv(this->mvp, 1, GL_FALSE, glm::value_ptr(mvp));
glLineWidth(3.0);
glDrawArrays(GL_LINES, 0, this->vertices_vector.size());
glDisableVertexAttribArray(this->coord3d);
glDisableVertexAttribArray(this->attrib_color);
glBindVertexArray(0);
}
void KikoBSP::cleanup_map()
{
/* delete[] textures;
delete[] planes;
delete[] this->leafs;
delete[] this->nodes;
delete[] this->leaf_this->faces;
delete[] this->models;
delete[] this->brushes;
delete[] this->brush_sides;
delete[] this->vertexes;
delete[] this->mesh_verts;
delete[] this->effects;
delete[] this->this->faces;
(use maybe later?) */
this->file.close();
}
There are no errors in the code, it just draws this:
Which is obviously not the map!
Also, heres my header file if you need that to:
#pragma once
#pragma pack(2)
#include <iostream>
#include <cstdint>
#include <string>
#include <vector>
#include <glm/vec3.hpp>
#include <fstream>
#include "shader.h"
#include <memory>
#include <array>
#define FACE_POLYGON 1
enum BSPLUMPS
{
ENTITIES,
TEXTURES,
PLANES,
NODES,
LEAFS,
LEAF_FACES,
LEAF_BRUSHES,
MODELS,
BRUSHES,
BRUSHSIDES,
VERTEXES,
MESHVERTS,
EFFECTS,
FACES,
LIGHTMAPS,
LIGHTVOLS,
VISDATA,
MAX_LUMPS
};
struct BSPLump
{
int32_t offset; /* offset to start of lump */
int32_t length; /* length of lump, always multiple of 4 */
};
struct BSPHeader
{
char magic[4]; /* ALWAYS IBSP */
int32_t version; /* 0x2E for Quake 3 */
BSPLump lumps[BSPLUMPS::MAX_LUMPS]; /* direntries */
};
struct BSPEntities
{
char* ents_array;
};
struct BSPTexture
{
char name[64];
int32_t flags;
int32_t contents;
};
struct BSPPlane
{
glm::vec3 normal;
float distance;
};
struct BSPNode
{
int32_t plane;
glm::ivec2 children;
glm::ivec3 mins;
glm::ivec3 maxs;
};
struct BSPLeaf
{
int32_t cluster;
int32_t area;
glm::ivec3 mins;
glm::ivec3 maxs;
int32_t leafface;
int32_t num_leaffaces;
int32_t leaf_brush;
int32_t num_leaf_brushes;
};
struct BSPLeafFace
{
int32_t face;
};
struct BSPLeafBrush
{
int32_t brush;
};
struct BSPModel
{
glm::fvec3 mins;
glm::fvec3 maxs;
int32_t face;
int32_t num_faces;
int32_t brush;
int32_t num_brushes;
};
struct BSPBrush
{
int32_t brush_side;
int32_t num_brush_sides;
int32_t texture;
};
struct BSPBrushSides
{
int32_t plane;
int32_t texture;
};
struct BSPVerts
{
glm::vec3 position;
glm::vec2 tex_coord; /* same as float tex_coord[2][2] */
glm::vec2 lm_coord; /* same as float tex_coord[2][2] */
glm::vec3 normal;
char color[4];
};
struct BSPMeshVerts
{
int32_t offset;
};
struct BSPEffects
{
char name[64];
int32_t brush;
int32_t unk; /* unknown */
};
struct BSPFaces
{
int32_t texture;
int32_t effect;
int32_t type;
int32_t vertex;
int32_t num_vertexes;
int32_t meshvert; /* start */
int32_t num_of_meshverts;
int32_t lm_index;
glm::ivec2 lm_start;
glm::ivec2 lm_size;
glm::vec3 lm_origin;
float lm_vecs[2][3];
glm::fvec3 normal;
glm::ivec2 size;
};
class KikoBSP
{
public:
bool load_map(std::string);
void render(glm::vec3);
void draw_level();
int32_t get_max();
int32_t get_min();
void get_vert();
void cleanup_map();
Shader shader;
BSPHeader header;
int32_t num_vertexes;
private:
std::ifstream file;
uint32_t vbo;
uint32_t vao;
uint32_t color_vbo;
uint32_t ebo;
int32_t coord3d;
int32_t mvp;
int32_t attrib_color;
BSPFaces* cur_face;
std::vector<float> vertices_vector;
std::vector<float> colors;
std::vector<float> indices;
std::vector<BSPFaces> faces;
std::vector<BSPTexture> textures;
std::vector<BSPPlane> planes;
std::vector<BSPNode> nodes;
std::vector<BSPMeshVerts> mesh_verts;
std::vector<BSPLeaf> leaf;
std::vector<BSPLeafFace> leaf_faces;
std::vector<BSPLeafBrush> leaf_brush;
std::vector<BSPModel> model;
std::vector<BSPBrush> brushes;
std::vector<BSPBrushSides> brush_sides;
std::vector<BSPEffects> effect;
int32_t num_textures;
int32_t num_planes;
int32_t num_nodes;
int32_t num_leafs;
int32_t num_leaf_faces;
int32_t num_leaf_brushes;
int32_t num_models;
int32_t num_brushes;
int32_t num_brush_sides;
int32_t num_meshverts;
int32_t num_effects;
int32_t num_faces;
int32_t num_lightmaps;
int32_t num_lightvols;
};
I think the problem is the indices or vertices. I believe the problem lies with how I STORE the data for the vertices. Its really buggy.
Thanks! All helps appreciated! :)
Whenever someone says "There are no errors in the code", you know something has already gone terribly wrong!
For example, what is this supposed to be doing?
for (int32_t x = 0; x < this->num_vertexes; x++)
{
vertexes.push_back(*reinterpret_cast<BSPVerts*>(&x));
}
It looks like it's invoking some undefined behavior here. If all you want to do is fill the array up, just do this:
vertexes.resize(num_vertices);
Secondly, the code that reads in the vertexes from the file looks like it has a logic error:
for (int32_t x = 0; x < this->num_vertexes; x++)
{
this->file.read(reinterpret_cast<char*>(&vertexes.data()[x]), this->header.lumps[BSPLUMPS::VERTEXES].length);
}
This looks like it's going to read in the entirety of the vertex lump for each vertex, probably resulting in bizarre behavior. You probably just want to do this:
file.read(reinterpret_cast<char*>(vertexes.data()), header.lumps[BSPLUMPS::VERTEXES].length);
Also, a note on your code that's unrelated to your problem: using this-> everywhere hurts readability and adds nothing to the code, remove it.
I did the same BSP renderer a while back, I can't remember what state I left it in, but you can have a look at the code here.
Related
I'm trying to create a class that saves BMP image given the pixels.
This is my .h:
#ifndef _IMAGE_SAVER_
#define _IMAGE_SAVER_
#include <vector>
#include <fstream>
#include <sstream>
#include <string>
#include <bitset>
//#define _DEBUG_
#ifdef _DEBUG_
#include <iostream>
#endif
#include <stdint.h>
typedef struct BMFileHeader {
uint16_t _bfType = 19778;
uint32_t _bfSize;
uint16_t _bfReserved1 = 0;
uint16_t _bfReserved2 = 0;
uint32_t _bfOffBits = 54;
} BMFileHeader;
typedef struct BMInfoHeader {
uint32_t _biSize = 40;
uint32_t _biWidth;
uint32_t _biHeight;
uint16_t _biPlanes = 1;
uint16_t _biBitCount = 24;
uint32_t _biCompression = 0;
uint32_t _biSizeImage = 0;
uint32_t _biXPelsPerMeter = 3780;
uint32_t _biYPelsPerMeter = 3780;
uint32_t _biClrUser = 0;
uint32_t _biClrImportant = 0;
} BMInfoHeader;
typedef struct RGBQuad {
uint8_t _blue;
uint8_t _green;
uint8_t _red;
} RGBQuad;
class ImageSaver
{
public:
ImageSaver() = delete;
ImageSaver(const uint32_t& height, const uint32_t& width, const std::vector<RGBQuad>& pixels) : _RGBQuad_Vector(pixels) {
this->_Info_Header._biHeight = height;
this->_Info_Header._biWidth = width;
};
void saveImage(const std::string& fileName);
protected:
void setFileSize();
void createImageFile(const std::string& fileName);
private:
BMFileHeader _File_Header;
BMInfoHeader _Info_Header;
std::vector<RGBQuad> _RGBQuad_Vector;
};
#endif
this is my .cpp:
#include "ImageSaver.h"
void ImageSaver::saveImage(const std::string& fileName)
{
this->setFileSize();
this->createImageFile(fileName);
}
void ImageSaver::setFileSize()
{
uint32_t height = this->_Info_Header._biHeight;
uint32_t width = this->_Info_Header._biWidth;
this->_File_Header._bfSize = 3 * (height * width) + 54;
}
void ImageSaver::createImageFile(const std::string& fileName)
{
std::ofstream imageFile(fileName + ".bmp", std::ios::binary);
imageFile.write((char*)&this->_File_Header, 14);
imageFile.write((char*)&this->_Info_Header, 40);
size_t numberOfPixels = this->_Info_Header._biHeight * this->_Info_Header._biWidth;
for (int i = 0; i < numberOfPixels; i++) {
imageFile.write((char*)&(this->_RGBQuad_Vector[i]), 3);
}
imageFile.close();
}
and this is my main.cpp:
#include "ImageSaver.h"
#include <vector>
#include <iostream>
int main() {
std::vector<RGBQuad> pixels;
for (unsigned i = 0; i < 512 * 512; i++) {
RGBQuad pixel;
pixel._blue = 0;
pixel._green = 255;
pixel._red = 255;
pixels.push_back(pixel);
}
ImageSaver im(512, 512, pixels);
im.saveImage("scene1");
std::cout << "ESTOP0" << std::endl;
return 0;
}
Whenever I try to create an image file, it says that that image file is corrupt, even though it seems to me that I've been following BMP format properly. I've analyzed raw binary data and data seems (to me) to be correct. GIMP can open the picture, but it gives a black 512x512 picture which is not what I'm going for here.
Certainly you miss #pragma pack. Wrap your structures with #pragma pack(push,1)/#pragma pack(pop)
When structures represent memory layout and should have no padding, even if having unaligned data, pack structures.
It is very helpful to have static_assert on expected sizeof value for such cases.
Additionally, when you pack them, the RGBQuad will become only 3 bytes. If you really want triple, suggest renaming it to triple. Otherwise add dummy or alpha byte to complete it to quad.
I'm working on a voxel engine in C++ using Vulkan. Most of the boilerplate code is heavily based on vulkan-tutorial.com. I have a drawFrame function that looks like this...
void drawFrame(float dt) {
vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX);
uint32_t imageIndex;
VkResult result = vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex);
updateUniformBuffer(imageIndex, dt);
if (result == VK_ERROR_OUT_OF_DATE_KHR) {
recreateSwapChain();
return;
} else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) {
throw std::runtime_error("failed to acquire swap chain image!");
}
// Check if a previous frame is using this image (i.e.there is its fence to wait on)
if (imagesInFlight[imageIndex] != VK_NULL_HANDLE) {
vkWaitForFences(device, 1, &imagesInFlight[imageIndex], VK_TRUE, UINT64_MAX);
}
// Mark the image as now being in use by this frame
imagesInFlight[imageIndex] = inFlightFences[currentFrame];
VkSubmitInfo submitInfo{};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
VkSemaphore waitSemaphores[] = { imageAvailableSemaphores[currentFrame] };
VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
submitInfo.waitSemaphoreCount = 1;
submitInfo.pWaitSemaphores = waitSemaphores;
submitInfo.pWaitDstStageMask = waitStages;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &commandBuffers[imageIndex];
VkSemaphore signalSemaphores[] = { renderFinishedSemaphores[currentFrame] };
submitInfo.signalSemaphoreCount = 1;
submitInfo.pSignalSemaphores = signalSemaphores;
vkResetFences(device, 1, &inFlightFences[currentFrame]);
result = vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]);
if (result != VK_SUCCESS) {
throw std::runtime_error("failed to submit draw command buffer!");
}
VkPresentInfoKHR presentInfo{};
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
presentInfo.waitSemaphoreCount = 1;
presentInfo.pWaitSemaphores = signalSemaphores;
VkSwapchainKHR swapChains[] = { swapChain };
presentInfo.swapchainCount = 1;
presentInfo.pSwapchains = swapChains;
presentInfo.pImageIndices = &imageIndex;
presentInfo.pResults = nullptr; // Optional
result = vkQueuePresentKHR(presentQueue, &presentInfo);
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || framebufferResized) {
framebufferResized = false;
recreateSwapChain();
} else if (result != VK_SUCCESS) {
throw std::runtime_error("failed to present swap chain image!");
}
// Increment the frame. By using the modulo(%) operator, we ensure that the frame index loops around after every MAX_FRAMES_IN_FLIGHT enqueued frames.
currentFrame = (currentFrame + 1) % config->maxFramesInFlight;
}
I'm passing in vertices like this...
void createVertexAndIndexBuffer() {
for (size_t x = 0; x < 100; x++) {
for (size_t y = 0; y < 4; y++) {
for (size_t z = 0; z < 100; z++) {
// for each block in the world vector
auto blockId = world.getBlock(x, y, z);
if (blockId == BlockId::Air) {
continue;
}
Vec3 blockPosition = { x, y, z };
// get its data
auto verts = blockdb.blockDataFor(blockId).getVertices();
auto inds = blockdb.blockDataFor(blockId).getIndices();
// account for the block position and store the new verts for later
for (int i = 0; i < verts.size(); i++) {
Vertex v(verts[i]);
v.pos += blockPosition;
vertices.push_back(v);
}
// store the indices for later accounting for the offset into the verts vector
for (int i = 0; i < inds.size(); i++) {
int ind(inds[i] + vertices.size());
indices.push_back(ind);
}
}
}
}
// time to start creating the actual buffer
VkDeviceSize vertexBufferSize = sizeof(vertices[0]) * vertices.size();
VkBuffer vertexStagingBuffer;
VkDeviceMemory vertexStagingBufferMemory;
createBuffer(vertexBufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, vertexStagingBuffer, vertexStagingBufferMemory);
void* vertexData;
vkMapMemory(device, vertexStagingBufferMemory, 0, vertexBufferSize, 0, &vertexData);
memcpy(vertexData, vertices.data(), (size_t)vertexBufferSize);
vkUnmapMemory(device, vertexStagingBufferMemory);
createBuffer(vertexBufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, vertexBuffer, vertexBufferMemory);
// use copyBuffer() to move the vertex data to the device local buffer
copyBuffer(vertexStagingBuffer, vertexBuffer, vertexBufferSize);
// After copying the data from the staging buffer to the device buffer, we should clean up the staging buffer since it is no longer needed.
vkDestroyBuffer(device, vertexStagingBuffer, nullptr);
vkFreeMemory(device, vertexStagingBufferMemory, nullptr);
// and do the same for the index buffer
VkDeviceSize indexBufferSize = sizeof(indices[0]) * indices.size();
VkBuffer indexStagingBuffer;
VkDeviceMemory indexStagingBufferMemory;
createBuffer(indexBufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, indexStagingBuffer, indexStagingBufferMemory);
void* indexData;
vkMapMemory(device, indexStagingBufferMemory, 0, indexBufferSize, 0, &indexData);
memcpy(indexData, indices.data(), (size_t)indexBufferSize);
vkUnmapMemory(device, indexStagingBufferMemory);
createBuffer(indexBufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, indexBuffer, indexBufferMemory);
copyBuffer(indexStagingBuffer, indexBuffer, indexBufferSize);
vkDestroyBuffer(device, indexStagingBuffer, nullptr);
vkFreeMemory(device, indexStagingBufferMemory, nullptr);
}
Everything works fine like that but I need be able to render by chunk instead of by block in order to implement chunk geometry optimizations. This is my chunk.h and chunk.cpp...
#pragma once
#include "Layer.h"
class Chunk {
public:
Chunk() = default;
Chunk(World* _world, Vec2XZ pos);
~Chunk() {}
BlockId getBlock(int x, int y, int z);
bool setBlock(BlockId id, int x, int y, int z);
bool isBlockOutOfBounds(int x, int y, int z);
void generateVerticesAndIndices();
void load();
std::array<Layer, CHUNK_HEIGHT> layers;
const Vec2XZ position;
const World* world;
bool isLoaded = false;
std::vector<Vertex> vertices;
std::vector<uint32_t> indices;
private:
};
#pragma once
#include "Chunk.h"
Chunk::Chunk(World* _world, Vec2XZ pos) :
position(pos),
world(_world) {
}
BlockId Chunk::getBlock(int x, int y, int z) {
if (isBlockOutOfBounds(x, y, z)) {
return BlockId::Air;
}
return layers[y].getBlock(x, z);
}
bool Chunk::setBlock(BlockId id, int x, int y, int z) {
if (!isBlockOutOfBounds(x, y, z)) {
if (layers[y].setBlock(id, x, z)) {
return true;
}
}
return false;
}
bool Chunk::isBlockOutOfBounds(int x, int y, int z) {
if (x >= CHUNK_WIDTH)
return true;
if (z >= CHUNK_WIDTH)
return true;
if (x < 0)
return true;
if (y < 0)
return true;
if (z < 0)
return true;
if (y >= CHUNK_HEIGHT) {
return true;
}
return false;
}
void Chunk::generateVerticesAndIndices() {
vertices.clear();
indices.clear();
for (int y = 0; y < CHUNK_HEIGHT; y++) {
for (int x = 0; x < CHUNK_WIDTH; x++) {
for (int z = 0; z < CHUNK_WIDTH; z++) {
// for each block in this chunk
auto blockId = getBlock(x, y, z);
if (blockId == BlockId::Air) {
continue; // dont render air
}
// infer the block position using its coordinates
Vec3 blockPosition = { x, y, z };
// get its data
auto verts = world->blockdb->blockDataFor(blockId).getVertices();
auto inds = world->blockdb->blockDataFor(blockId).getIndices();
// account for the block position and store the new verts
for (int i = 0; i < verts.size(); i++) {
Vertex v(verts[i]);
v.pos += blockPosition;
vertices.push_back(v);
}
// store the indices for later accounting for the offset into the verts vector
for (int i = 0; i < inds.size(); i++) {
int ind(inds[i] + vertices.size());
indices.push_back(ind);
}
}
}
}
}
void Chunk::load() {
if (isLoaded) {
return;
}
// todo: actual terrain generation
for (int y = 0; y < 4; y++) {
for (int x = 0; x < CHUNK_WIDTH; x++) {
for (int z = 0; z < CHUNK_WIDTH; z++) {
setBlock(BlockId::Grass, x, y, z);
}
}
}
isLoaded = true;
}
So I've basically migrated the top part of createVertexAndIndexBuffer() over to the chunk class. Then within createVertexAndIndexBuffer(), I iterate through the chunks around the player within render distance like this...
void createVertexAndIndexBuffer() {
// set bounds of how far out to render based on what chunk the player is in
Vec2XZ playerChunkCoords = { floor(player.position.x) / CHUNK_WIDTH, floor(player.position.z) / CHUNK_WIDTH };
Vec2XZ lowChunkXZ = { playerChunkCoords.x - renderDistance, playerChunkCoords.z - renderDistance };
Vec2XZ highChunkXZ = { playerChunkCoords.x + renderDistance, playerChunkCoords.z + renderDistance };
// for each chunk around the player within render distance
for (int x = lowChunkXZ.x; x < highChunkXZ.x; x++) {
for (int z = lowChunkXZ.z; z < highChunkXZ.z; z++) {
// get the chunk
Chunk* chunk = &world.getChunk(x, z);
// load it if it isnt already
if (!chunk->isLoaded) {
chunk->load();
}
// generate its geometry if it doesnt already exist
if (chunk->vertices.size() == 0 || chunk->indices.size() == 0) {
chunk->generateVerticesAndIndices();
}
auto verts = chunk->vertices;
auto inds = chunk->indices;
// account for the chunk position and store the new verts for later
for (int i = 0; i < verts.size(); i++) {
Vertex v(verts[i]);
v.pos.x += x * CHUNK_WIDTH;
v.pos.z += z * CHUNK_WIDTH;
vertices.push_back(v);
}
// store the indices for later accounting for the offset into the verts vector
for (int i = 0; i < inds.size(); i++) {
int ind(inds[i] + vertices.size());
indices.push_back(ind);
}
}
}
// time to start creating the actual buffer
VkDeviceSize vertexBufferSize = sizeof(vertices[0]) * vertices.size();
VkBuffer vertexStagingBuffer;
VkDeviceMemory vertexStagingBufferMemory;
createBuffer(vertexBufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, vertexStagingBuffer, vertexStagingBufferMemory);
void* vertexData;
vkMapMemory(device, vertexStagingBufferMemory, 0, vertexBufferSize, 0, &vertexData);
memcpy(vertexData, vertices.data(), (size_t)vertexBufferSize);
vkUnmapMemory(device, vertexStagingBufferMemory);
createBuffer(vertexBufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, vertexBuffer, vertexBufferMemory);
// use copyBuffer() to move the vertex data to the device local buffer
copyBuffer(vertexStagingBuffer, vertexBuffer, vertexBufferSize);
// After copying the data from the staging buffer to the device buffer, we should clean up the staging buffer since it is no longer needed.
vkDestroyBuffer(device, vertexStagingBuffer, nullptr);
vkFreeMemory(device, vertexStagingBufferMemory, nullptr);
// and do the same for the index buffer
VkDeviceSize indexBufferSize = sizeof(indices[0]) * indices.size();
VkBuffer indexStagingBuffer;
VkDeviceMemory indexStagingBufferMemory;
createBuffer(indexBufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, indexStagingBuffer, indexStagingBufferMemory);
void* indexData;
vkMapMemory(device, indexStagingBufferMemory, 0, indexBufferSize, 0, &indexData);
memcpy(indexData, indices.data(), (size_t)indexBufferSize);
vkUnmapMemory(device, indexStagingBufferMemory);
createBuffer(indexBufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, indexBuffer, indexBufferMemory);
copyBuffer(indexStagingBuffer, indexBuffer, indexBufferSize);
vkDestroyBuffer(device, indexStagingBuffer, nullptr);
vkFreeMemory(device, indexStagingBufferMemory, nullptr);
}
With this code, the engine starts up fine but the screen stays white and then after a few calls to vkQueueSubmit() within drawFrame(), vkQueueSubmit() returns VK_ERROR_DEVICE_LOST instead of VK_SUCCESS and then the app throws the corresponding runtime error, prints out the corresponding debug information "failed to submit draw command buffer!", waits for me to press a key, and then finally terminates with EXIT_FAILURE.
Why does pushing vertices from blocks directly work fine, but pushing them from chunks does not? I have checked the Vulkan specification and did a lot of googling but I just couldn't find much on what causes this error to be thrown. I want to know how to fix it and in turn, fix my engine.
I was assigning indices incorrectly and it was being caught by the graphics driver. There were indices that were larger than the vertex vector.
I'm working on a project. Its essence is reading and writing increased BMP files and must do it using binary files.
I have a problem with reading the height and width of the image.
If I put a 1x1 image in the program, it says that the size of the image is 65kx65k, which is totally wrong. Here is my code. I need help with it.
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
typedef struct {
int8_t id[2]; // Завжди дві літери 'B' і 'M'
int32_t filesize; // Розмір файла в байтах
int16_t reserved[2]; // 0, 0
int32_t headersize; // 54L для 24-бітних зображень
int32_t infoSize; // 40L для 24-бітних зображень
int32_t width; // ширина зображення в пікселях
int32_t depth; // висота зображення в пікселях
int16_t biPlanes; // 1 (для 24-бітних зображень)
int16_t bits; // 24 (для 24-бітних зображень)
int32_t biCompression; // 0L
int32_t biSizeImage; // Можна поставити в 0L для зображень без компрессії (наш варіант)
int32_t biXPelsPerMeter; // Рекомендована кількість пікселів на метр, можна 0L
int32_t biYPelsPerMeter; // Те саме, по висоті
int32_t biClrUsed; // Для індексованих зображень, можна поставити 0L
int32_t biClrImportant; // Те саме
} BMPHEAD;
typedef struct {
int8_t redComponent;
int8_t greenComponent;
int8_t blueComponent;
} PIXELDATA;
const int width = 65;
const int height = 65;
int main() {
FILE* in = fopen("test.bmp", "rb");
if (!in) { cout << "Something wrong with input file"; exit(1); }
FILE* out = fopen("out.bmp", "wb");
if(!out){cout << "Something wrong with output file"; exit(2); }
BMPHEAD head;
fread(&head, sizeof(BMPHEAD), 1, in);
fwrite(&head, sizeof(BMPHEAD), 1, out);
PIXELDATA p;
for (int i = 1; i <= height; i++) {
for (int j = 1; j <= width; j++) {
fread(&p, sizeof(PIXELDATA), 1, in);
fwrite(&p, sizeof(PIXELDATA), 1, out);
}
for (int j = 1; j <= (sizeof(PIXELDATA) * width) % 4; j++)
fputc(0x00, out);
}
fclose(in);
fclose(out);
cout << sizeof(PIXELDATA);
}
The shader takes an SSBO of Photons that have a position, direction, wavelength and intensity and each thread is responsible for tracing exactly one photon through the grid, where at each grid cell the photon hits, the intensity is accumulated for each wavelength to create a spectral distribution for each grid cell.
The problem is that the shader works perfectly for 100,000 photons, but doesn't return a result for 1,000,000 photons.
I looked into the sizes for the SSBOs and all were within my GPUs (NVIDIA Quadro P6000) limits of 2GB:
SSBO Grid size: 1.5GB
SSBO Photons Size: 0.02GB
If I change the logic at some places it works with one million photons (see lines 87 and 114 for comments).
I currently can't see any explanation of why the shader fails for 1,000,000 photons, but works for 100,000 photons. The logic is the same and the buffer sizes are within limits. (That the buffer size can't be a problem is also confirmed by that it works when changing the logic.)
Below is the source code. If you want to try it yourself here is the code on github: https://github.com/TheJhonny007/TextureTracerDebug
Compute Shader:
#version 430
#extension GL_EXT_compute_shader: enable
#extension GL_EXT_shader_storage_buffer_object: enable
#extension GL_ARB_compute_variable_group_size: enable
const uint TEX_WIDTH = 1024u;
const uint TEX_HEIGHT = TEX_WIDTH;
const uint MIN_WAVELENGTH = 380u;
const uint MAX_WAVELENGTH = 740u;
const uint NUM_WAVELENGTHS = MAX_WAVELENGTH - MIN_WAVELENGTH;
// Size: 24 bytes -> ~40,000,000 photons per available gigabyte of ram
struct Photon {
vec2 position;// m
vec2 direction;// normalized
uint wavelength;// nm
float intensity;// 0..1 should start at 1
};
layout(std430, binding = 0) buffer Photons {
Photon photons[];
};
// Size: 1440 bytes -> ~700,000 pixels per available gigabyte of ram
struct Pixel {
uint intensityAtWavelengths[NUM_WAVELENGTHS];// [0..1000]
};
layout(std430, binding = 1) buffer Pixels {
//Pixel pixels[TEX_WIDTH][TEX_HEIGHT];
// NVIDIAs linker takes ages to link if the sizes are specified :(
Pixel[] pixels;
};
uniform float xAxisScalingFactor;
vec2 getHorizontalRectangleAt(int i) {
float x = pow(float(i), xAxisScalingFactor);
float w = pow(float(i + 1), xAxisScalingFactor);
return vec2(x, w);
}
uniform float rectangleHeight;
struct Rectangle {
float x;
float y;
float w;
float h;
};
layout (local_size_variable) in;
void addToPixel(uvec2 idx, uint wavelength, uint intensity) {
if (idx.x >= 0u && idx.x < TEX_WIDTH && idx.y >= 0u && idx.y < TEX_HEIGHT) {
uint index = (idx.y * TEX_WIDTH) + idx.x;
atomicAdd(pixels[index].intensityAtWavelengths[wavelength - MIN_WAVELENGTH], intensity);
}
}
/// Returns the rectangle at the given indices.
Rectangle getRectangleAt(ivec2 indices) {
vec2 horRect = getHorizontalRectangleAt(indices.x);
return Rectangle(horRect.x, rectangleHeight * float(indices.y), horRect.y, rectangleHeight);
}
uniform float shadowLength;
uniform float shadowHeight;
/// Returns the indices of the rectangle at the given location
ivec2 getRectangleIdxAt(vec2 location) {
int x = 0;
int y = int(location.y / rectangleHeight);
return ivec2(x, y);
}
float getRayIntersectAtX(Photon ray, float x) {
float slope = ray.direction.y / ray.direction.x;
return slope * (x - ray.position.x) + ray.position.y;
}
ivec2 getRayRectangleExitEdge(Photon ray, Rectangle rect) {
float intersectHeight = getRayIntersectAtX(ray, rect.x + rect.w);
// IF ONE OF THE FIRST TWO CONDITIONS GETS REMOVED IT WORKS WITH 1'000'000 PHOTONS OTHERWISE ONLY 100'000 WHY?
if (intersectHeight < rect.y) {
return ivec2(0, -1);
} else if (intersectHeight > rect.y + rect.h) {
return ivec2(0, 1);
} else {
return ivec2(1, 0);
}
}
void main() {
uint gid = gl_GlobalInvocationID.x;
if (gid >= photons.length()) return;
Photon photon = photons[gid];
ivec2 photonTexIndices = getRectangleIdxAt(photon.position);
while (photonTexIndices.x < TEX_WIDTH && photonTexIndices.y < TEX_HEIGHT &&
photonTexIndices.x >= 0 && photonTexIndices.y >= 0) {
// need to convert to uint for atomic add operations...
addToPixel(uvec2(photonTexIndices), photon.wavelength, uint(photon.intensity * 100.0));
ivec2 dir = getRayRectangleExitEdge(photon, getRectangleAt(photonTexIndices));
photonTexIndices += dir;
// When the ray goes out of bounds on the bottom then mirror it to simulate rays coming from
// the other side of the planet. This works because of the rotational symmetry of the system.
// IF COMMENTET OUT IT WORKS WITH 1'000'000 PHOTONS OTHERWISE ONLY 100'000 WHY?
if (photonTexIndices.y < 0) {
photonTexIndices.y = 0;
photon.position.y *= -1.0;
photon.direction.y *= -1.0;
}
}
}
Tracer.hpp
#ifndef TEXTURE_TRACER_HPP
#define TEXTURE_TRACER_HPP
#include <glm/glm.hpp>
#include <random>
namespace gpu {
// 6 * 4 = 24 Bytes
struct Photon {
glm::vec2 position; // m
glm::vec2 direction; // normalized
uint32_t waveLength; // nm
float intensity; // 0..1 should start at 1
};
class TextureTracer {
public:
TextureTracer();
uint32_t createShadowMap(size_t numPhotons);
private:
void initTextureTracer();
void traceThroughTexture(uint32_t ssboPhotons, size_t numPhotons);
Photon emitPhoton();
std::vector<Photon> generatePhotons(uint32_t count);
struct {
uint32_t uRectangleHeight;
uint32_t uShadowLength;
uint32_t uShadowHeight;
uint32_t uXAxisScalingFactor;
} mTextureTracerUniforms;
uint32_t mTextureTracerProgram;
std::mt19937_64 mRNG;
std::uniform_real_distribution<> mDistributionSun;
std::uniform_int_distribution<uint32_t> mDistributionWavelength;
std::bernoulli_distribution mDistributionBoolean;
};
} // namespace gpu
#endif // TEXTURE_TRACER_HPP
Tracer.cpp
#include "TextureTracer.hpp"
#include <GL/glew.h>
#include <algorithm>
#include <fstream>
#include <iostream>
#include <random>
#include <string>
#include <vector>
void GLAPIENTRY MessageCallback(GLenum source, GLenum type, GLuint id,
GLenum severity, GLsizei length,
const GLchar *message, const void *userParam) {
if (type == GL_DEBUG_TYPE_ERROR)
fprintf(stderr, "GL ERROR: type = 0x%x, severity = 0x%x, message = %s\n",
type, severity, message);
else
fprintf(stdout, "GL INFO: type = 0x%x, severity = 0x%x, message = %s\n",
type, severity, message);
}
namespace gpu {
const double TEX_HEIGHT_TO_RADIUS_FACTOR = 4;
const double TEX_SHADOW_LENGTH_FACTOR = 8;
const uint32_t TEX_WIDTH = 1024u;
const uint32_t TEX_HEIGHT = TEX_WIDTH;
const double RADIUS = 6'371'000.0;
const double RADIUS_FACTORED = RADIUS * TEX_HEIGHT_TO_RADIUS_FACTOR;
const double SUN_RADIUS = 695'510'000.0;
const double DIST_TO_SUN = 149'600'000'000.0;
const double ATMO_HEIGHT = 42'000.0;
std::string loadShader(const std::string &fileName) {
std::ifstream shaderFileStream(fileName, std::ios::in);
if (!shaderFileStream.is_open()) {
std::cerr << "Could not load the GLSL shader from '" << fileName << "'!"
<< std::endl;
exit(-1);
}
std::string shaderCode;
while (!shaderFileStream.eof()) {
std::string line;
std::getline(shaderFileStream, line);
shaderCode.append(line + "\n");
}
return shaderCode;
}
void TextureTracer::initTextureTracer() {
mTextureTracerProgram = glCreateProgram();
uint32_t rayTracingComputeShader = glCreateShader(GL_COMPUTE_SHADER);
std::string code = loadShader("../resources/TextureTracer.glsl");
const char *shader = code.c_str();
glShaderSource(rayTracingComputeShader, 1, &shader, nullptr);
glCompileShader(rayTracingComputeShader);
glAttachShader(mTextureTracerProgram, rayTracingComputeShader);
glLinkProgram(mTextureTracerProgram);
mTextureTracerUniforms.uRectangleHeight =
glGetUniformLocation(mTextureTracerProgram, "rectangleHeight");
mTextureTracerUniforms.uShadowHeight =
glGetUniformLocation(mTextureTracerProgram, "shadowHeight");
mTextureTracerUniforms.uShadowLength =
glGetUniformLocation(mTextureTracerProgram, "shadowLength");
mTextureTracerUniforms.uXAxisScalingFactor =
glGetUniformLocation(mTextureTracerProgram, "xAxisScalingFactor");
glDetachShader(mTextureTracerProgram, rayTracingComputeShader);
glDeleteShader(rayTracingComputeShader);
}
TextureTracer::TextureTracer()
: mRNG(1L), mDistributionSun(
std::uniform_real_distribution<>(-SUN_RADIUS, SUN_RADIUS)),
mDistributionWavelength(
std::uniform_int_distribution<uint32_t>(380, 739)),
mDistributionBoolean(std::bernoulli_distribution(0.5)) {
glEnable(GL_DEBUG_OUTPUT);
glDebugMessageCallback(MessageCallback, nullptr);
initTextureTracer();
}
double raySphereDistance(glm::dvec2 origin, glm::dvec2 direction,
glm::dvec2 center, double radius) {
glm::dvec2 m = origin - center;
double b = glm::dot(m, direction);
double c = glm::dot(m, m) - (radius * radius);
if (c > 0.0 && b > 0.0)
return -1.0;
double discr = b * b - c;
// A negative discriminant corresponds to ray missing sphere
if (discr < 0.0)
return -1.0;
// Ray now found to intersect sphere, compute smallest t value of intersection
return glm::max(0.0, -b - glm::sqrt(discr));
}
Photon TextureTracer::emitPhoton() {
std::uniform_real_distribution<> distributionEarth(0.0, ATMO_HEIGHT);
glm::dvec2 target = {0.0, RADIUS + distributionEarth(mRNG)};
double d;
do {
d = glm::length(glm::dvec2(mDistributionSun(mRNG), mDistributionSun(mRNG)));
} while (d > SUN_RADIUS);
glm::dvec2 startPosition =
glm::dvec2(-DIST_TO_SUN, mDistributionBoolean(mRNG) ? d : -d);
glm::dvec2 direction = glm::normalize(target - startPosition);
startPosition +=
direction * raySphereDistance(startPosition, direction, {0.0, 0.0},
RADIUS + ATMO_HEIGHT);
return {glm::vec2(0.0, startPosition.y), glm::vec2(direction),
mDistributionWavelength(mRNG), 1.0f};
}
std::vector<Photon> TextureTracer::generatePhotons(uint32_t count) {
std::vector<Photon> photons(count);
std::generate(photons.begin(), photons.end(),
[this]() { return emitPhoton(); });
return photons;
}
void TextureTracer::traceThroughTexture(uint32_t ssboPhotons,
size_t numPhotons) {
glUseProgram(mTextureTracerProgram);
glUniform1f(mTextureTracerUniforms.uRectangleHeight,
RADIUS_FACTORED / TEX_HEIGHT);
const double shadowLength =
TEX_SHADOW_LENGTH_FACTOR * (DIST_TO_SUN * RADIUS) / (SUN_RADIUS - RADIUS);
glUniform1f(mTextureTracerUniforms.uShadowLength, shadowLength);
glUniform1f(mTextureTracerUniforms.uShadowHeight, RADIUS_FACTORED);
const double xAxisScalingFactor =
glm::log(shadowLength) / glm::log(static_cast<double>(TEX_WIDTH));
glUniform1f(mTextureTracerUniforms.uXAxisScalingFactor,
static_cast<float>(xAxisScalingFactor));
const uint32_t MIN_WAVELENGTH = 380u;
const uint32_t MAX_WAVELENGTH = 740u;
const uint32_t NUM_WAVELENGTHS = MAX_WAVELENGTH - MIN_WAVELENGTH;
size_t pixelBufferSize =
TEX_WIDTH * TEX_HEIGHT * NUM_WAVELENGTHS * sizeof(uint32_t);
uint32_t ssboPixels;
glGenBuffers(1, &ssboPixels);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssboPixels);
glBufferData(GL_SHADER_STORAGE_BUFFER, pixelBufferSize, nullptr,
GL_DYNAMIC_COPY);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, ssboPhotons);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, ssboPixels);
const uint32_t numThreads = 32u;
const uint32_t numBlocks = numPhotons / numThreads;
std::cout << "numBlocks: " << numBlocks << std::endl;
glDispatchComputeGroupSizeARB(numBlocks, 1, 1, numThreads, 1, 1);
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
struct Pixel {
uint32_t intensityAtWavelengths[NUM_WAVELENGTHS];
};
std::vector<Pixel> pixels(TEX_WIDTH * TEX_HEIGHT);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssboPixels);
glGetBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, pixelBufferSize,
pixels.data());
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
for (int y = 0; y < TEX_HEIGHT; ++y) {
printf("%4i | ", y);
for (int x = 0; x < TEX_WIDTH; ++x) {
Pixel p = pixels[y * TEX_WIDTH + x];
int counter = 0;
for (uint32_t i : p.intensityAtWavelengths) {
counter += i;
}
if (counter == 0) {
printf(" ");
} else if (counter > 100'000'000) {
printf("%4s", "\u25A0");
} else if (counter > 10'000'000) {
printf("%4s", "\u25A3");
} else if (counter > 1'000'000) {
printf("%4s", "\u25A6");
} else if (counter > 100'000) {
printf("%4s", "\u25A4");
} else {
printf("%4s", "\u25A1");
}
}
std::cout << std::endl;
}
glDeleteBuffers(1, &ssboPixels);
glUseProgram(0);
}
uint32_t TextureTracer::createShadowMap(size_t numPhotons) {
std::vector<Photon> photons = generatePhotons(numPhotons);
uint32_t ssboPhotons;
glGenBuffers(1, &ssboPhotons);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssboPhotons);
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(Photon) * photons.size(),
photons.data(), GL_DYNAMIC_COPY);
traceThroughTexture(ssboPhotons, photons.size());
glDeleteBuffers(1, &ssboPhotons);
glDeleteProgram(mTextureTracerProgram);
glDisable(GL_DEBUG_OUTPUT);
glDebugMessageCallback(nullptr, nullptr);
return 0;
}
}
main.cpp
#include <GL/glew.h>
#include <GL/glut.h>
#include "TextureTracer.hpp"
int main(int argc, char *argv[]) {
glutInit(&argc, argv);
glutCreateWindow("OpenGL needs a window o.O");
glewInit();
auto mapper = gpu::TextureTracer();
// WITH 100'000 PHOTONS IT WORKS, WITH 1'000'000 PHOTONS NOT WHY?
mapper.createShadowMap(100'000);
return 0;
}
Operating systems cancel GPU program executions if they take too long. On Windows it is generally two seconds and on Linux it is five seconds most of the time, but it can vary.
This is to detect GPU programs that are stuck and cancel them. There are different methods to get around this timeout, but they all require admin/root privileges, which is not always available.
If possible the execution can be split up into multiple invocations like in the following snippet:
const uint32_t passSize = 2048u;
const uint32_t numPasses = (numPhotons / passSize) + 1;
const uint32_t numThreads = 64u;
const uint32_t numBlocks = passSize / numThreads;
glUniform1ui(glGetUniformLocation(mTextureTracerProgram, "passSize"), passSize);
for (uint32_t pass = 0u; pass < numPasses; ++pass) {
glUniform1ui(glGetUniformLocation(mTextureTracerProgram, "pass"), pass);
glDispatchComputeGroupSizeARB(numBlocks, 1, 1, numThreads, 1, 1);
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
glFlush();
glFinish();
}
The glFlush() and glFinish() calls are important or the executions will get bundled together and the OS triggers a timeout anyways.
In the shader you just need to access the right sections of the input data like so:
// other stuff
uniform uint pass;
uniform uint passSize;
void main() {
uint gid = gl_GlobalInvocationID.x;
uint passId = pass * passSize + gid;
if (passId >= photons.length()) return;
Photon photon = photons[passId];
// rest of program
}
This is all.
If you want to disable the OS timeouts here is a relevant post for Linux: https://stackoverflow.com/a/30520538/5543884
And here is a post regarding Windows: https://stackoverflow.com/a/29759823/5543884
I am trying to write a function which will change all RGB(255,0,0) pixels to RGB(255,0,255) but it doesn't seem to work :D Could anyone tell me the reason why it doesn't work/show me the right way to do it. All criticism all appreciated.
Here is part of my code:
#include <stdio.h>
#include <Windows.h>
#include <stdint.h>
struct BmpSignature
{
unsigned char data[2];
};
#pragma pack(1)
struct BmpHeader
{
BmpSignature signature;
uint32_t fileSize;
uint16_t reserved1;
uint16_t reserved2;
uint32_t dataOffset;
};
struct BmpDib
{
uint32_t dibSize;
int32_t imageWidth;
int32_t imageHeight;
uint16_t colorPlaneCount;
uint16_t pixelSize;
uint32_t compressMethod;
uint32_t bitmapByteCount;
int32_t horizontalResolution;
int32_t verticalResolution;
uint32_t colorCount;
uint32_t importantColorCount;
};
struct Color
{
unsigned char blue;
unsigned char green;
unsigned char red;
};
struct PixelArray
{
Color **pixels;
uint32_t rowCount;
uint32_t columnCount;
};
void changeBmp(PixelArray &p)
{
for (int i = 0; i < p.rowCount; i++)
for (int j = 0; j < p.columnCount; j++)
{
if (p.pixels[i][j].red == 255 && p.pixels[i][j].green == 0 && p.pixels[i][j].blue == 0)
{
p.pixels[i][j].blue = 255;
}
}
}
void readBmpPixelArray(FILE *f, BmpHeader header, BmpDib dib, PixelArray &data)
{
if (f == NULL)
return;
data.rowCount = dib.imageHeight;
data.columnCount = dib.imageWidth;
data.pixels = new Color*[data.rowCount];
char paddingCount = (4 - (dib.imageWidth * (dib.pixelSize / 8) % 4)) % 4;
fseek(f, header.dataOffset, 0L);
for (int i = 0; i < data.rowCount; i++)
{
scanBmpPixelLine(f, data.pixels[data.rowCount - i - 1], dib.imageWidth);
skipBmpPadding(f, paddingCount);
}
}
void writeBmpPixelArray(FILE *f, BmpHeader header, BmpDib dib, PixelArray &data)
{
if (f == NULL)
return;
data.rowCount = dib.imageHeight;
data.columnCount = dib.imageWidth;
char paddingCount = (4 - (dib.imageWidth * (dib.pixelSize / 8) % 4)) % 4;
fseek(f, header.dataOffset, 0L);
for (int i = 0; i < data.rowCount; i++)
{
writeBmpPixelLine(f, data.pixels[data.rowCount - i - 1], dib.imageWidth);
addBmpPadding(f, paddingCount);
}
}
void writeBmpPixelLine(FILE *f, Color *&line, uint32_t length)
{
if (f == NULL)
return;
fwrite(line, sizeof(Color), length, f);
}
void scanBmpPixelLine(FILE *f, Color *&line, uint32_t length)
{
if (f == NULL)
return;
line = new Color[length];
fread(line, sizeof(Color), length, f);
}
void addBmpPadding(FILE *f, char count)
{
if (f == NULL)
return;
if (count == 0)
return;
char padding[3];
fwrite(padding, count, 1, f);
}
void skipBmpPadding(FILE *f, char count)
{
if (f == NULL)
return;
if (count == 0)
return;
char padding[3];
fread(padding, count, 1, f);
}
void drawBmp(BmpDib dib, PixelArray data)
{
HWND console = GetConsoleWindow();
HDC hdc = GetDC(console);
for (int i = 0; i < dib.imageHeight; i++)
for (int j = 0; j < dib.imageWidth; j++)
{
Color pixel = data.pixels[i][j];
SetPixel(hdc, j, i, RGB(pixel.red, pixel.green, pixel.blue));
}
ReleaseDC(console, hdc);
}
void releaseBmpPixelArray(PixelArray data)
{
for (int i = 0; i < data.rowCount; i++)
delete[]data.pixels[i];
delete[]data.pixels;
}
int main()
{
BmpHeader header;
BmpDib info;
PixelArray data;
FILE * inputBMP;
inputBMP = fopen("D:\\Projects\\bitmap\\yoyo.bmp", "rb");
readBmpHeader(inputBMP, header);
printBmpHeader(header);
readBmpDib(inputBMP, info);
printBmpDib(info);
readBmpPixelArray(inputBMP, header, info, data);
HWND console = GetConsoleWindow();
if (console != NULL){ MoveWindow(console, 0, 0, 800, 600, TRUE); }
changeBmp(data);
drawBmp(info, data);
releaseBmpPixelArray(data);
fclose(inputBMP);
Sleep(100);
return 0;
}