Jump to content
  • Advertisement
  • entries
  • comments
  • views

Here comes the sun!



It’s been a while since my last update. Part of the reason was because I was sitting in a Russian hospital for two months. But I’ll leave that story for another time…… However I did manage to make quite a bit of progress.  I had some adventures in threading….. I had to rewrite my thread code like four times, but eventually I got it under control so things are running a LOT faster now. I’ll try to cover that in my next update.   Also I added the makings of lighting system, which brings me to today’s topic.

Since I wanted to start shading my planet, I of course needed a light. But I didn’t want some non-descript photons coming invisibly in from outer space.  A proper planet should orbit a sun!  So before doing the lighting I decided to make one. No problem, I had my trusty voxel sphere engine! ….

But wait!.... A voxel built sphere seemed like a waste of computing resources for just doing a sun.  I mean a sun doesn’t have overhangs and underground caverns. It’s just really a ball in space.  That being the case, I decided to add simple terrain mesh capability to my library.  On the other hand, to make it useful for other things, such as space games where a ship might get close to the sun, I decided to incorporate LOD, chunking and a few bells and whistles.  

The implementation parallels the voxel code pretty closely, except everything is scaled down to 2D from 3D. For instance instead of organizing my mesh into an octree, I use a quadtree.  The bounding shape is a pill instead of a sphere. This is because with height-mapped terrain, triangles can stretch a lot in the vertical direction and so spheres are really sub-optimal as they don’t enclose the geometry very tightly. For the sun it doesn’t matter, but I like to make stuff reusable. For example I could use this terrain code to build a simple moon. Like the voxel library it accepts functions that can generate terrain…..but back to the sun…..

For shading the sun, I’m using 5D noise, 3D for the object itself and 2D for time.  For the purposes of my noise function, time kind of sweeps around in a circle so I don’t run into precision problems on the GPU.  I could probably have done this with 4D noise and some tiling features, but there are some issues with that and I decided it was less of a headache to just go with 5D noise.

To do this you basically you take the modulus of linear time with your desired repetition interval, convert the result to radians such that 2XPi radians maps to your interval.  Then you take the sin and cos of that, and multiply both by some scaling factor, and feed that into two dimensions of your noise function.  The bigger the scaling factor, the faster your noise changes.

Right now it looks kind of basic, but later I’ll try to make it look more sun like by applying some other functions to the raw noise and maybe adding a few more octaves.  Also I was reading about god ray shaders and I think that might look good in combination with the noise functions.   

Here’s the video:

Hopefully for my next update I’ll have the planet orbiting the sun. And after that I plan on grabbing the JIT collision code from my old project and we’ll be down on the surface!

1 Comment

Recommended Comments

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Advertisement
  • Advertisement
  • Blog Entries

  • Similar Content

    • By SuperVGA
      A little backstory:
      I'm tessellating some simple terrain. I have some patches which are modulo translated up to their own size, so the patches are repeated while giving an effect of being an endless supply of patches to either side. I'd like to only translate them up to one unit instead, so that the far reaches of all my patches don't change by the full patch size, but only by one unit.
      However, the winding order of the triangles differ, and 4 texels in the heightmap render different depending on the winding order, so I'd like to enforce the winding order for all the generated triangles.
      Is there any way to dictate the triangle winding order for all the generated geometry? I'd like for them to be similar, not flipped around the center of the patch...
      Thanks in advance,

    • By svetpet
      Helllo, I am multithreading large piece of code (gcc 7.4, c++17) and I stumbled upon a lot of race conditions when accessing std::shared_ptr. 
      I tried using boost::atomic_shared_ptr, but there are some critical parts of the code where shared pointers are returned from functions, or shared pointers are used in constructors of other classes, which is not supported from boost::atomic_shared_ptr (copy constructor and assignment operator are private). 
      I had 3 other suggestions: 
      -to wrap every place where shared pointer is accessed with mutex, but this looks like overkill to me.
      -to create my implementation of atomic_shared_pointer. 
      -to use std::atomic_store/std::atomic_load. 
      What is the best way to make the access to the shared pointers thread-safe? Is there something special to consider when returning shared pointer from function, related to atomicity?
    • By Alex Gomez Cortes
      Hello everyone,
      I write this because I need some help in order to achieve a proper shadowmap. I am trying to render some shadows but I am stucked. 
      What I do is create a framebuffer and a texture where I will paint the shadowmap. I tell openGL to only take into account the depth information, and the same for the texture.
      this is the code for generating the framebuffer and the texture.

      This is what my shadowmap looks like. I know that the shadowmap texture is grayscaled but I do not think this is good.
      Thank you for your help!
    • By Charlie Malmqvist
      I'm trying to implement model loading, and so far the meshes and most vertex data is imported correctly. However, some meshes seems to get the uv's wrong (as seen in attached screenshot) and I don't really have any idea how to debug this. Below are the shaders and the model class which does the loading with assimp. The whole project can be found at https://github.com/N00TN00T/Wizzy.

      // VERTEX SHADER layout(location = 0) in vec4 vertexPosition; layout(location = 1) in vec2 vertexUv; layout(location = 2) in vec3 vertexNormal; uniform mat4 camTransform; uniform mat4 worldTransform; out vec4 worldPosition; out vec2 uv; void main() { worldPosition = camTransform * worldTransform * vertexPosition; gl_Position = worldPosition; uv = vertexUv; } // FRAGMENT SHADER out vec4 outColor; in vec4 worldPosition; in vec2 uv; uniform vec4 albedo = vec4(1.0, 1.0, 1.0, 1.0); uniform vec4 diffuseColor = vec4(1.0, 1.0, 1.0, 1.0); uniform bool useDiffuseMap = false; uniform sampler2D diffuseMap; void main() { vec4 textureColor = vec4(1.0, 1.0, 1.0, 1.0); if (useDiffuseMap) { textureColor = texture(diffuseMap, uv); } outColor = textureColor * diffuseColor * albedo; } Model class:
      // model.h class Model : public Resource { public: Model(const string& sourceFile, const string& data, const ulib::Bitset& flags); inline std::vector<Mesh>& GetMeshes() { return m_meshes; } virtual string Serialize() const override; static Model* Create(const string& sourceFile, const string& data, ulib::Bitset flags = ulib::Bitset()); private: bool VerifyAssimpScene(const aiScene *scene, string *error); bool VerifyAssimpMesh(const aiMesh *mesh, string *error); bool ProcessNode(aiNode *node, const aiScene *scene, const string& sourceFile); Mesh ProcessMesh(aiMesh *mesh, const aiScene *scene, bool *success, const string& sourceFile, int32 meshIndex); MaterialHandle ProcessMaterial(aiMaterial *mat, const string& meshName, const aiScene *scene, const string& sourceFile); std::vector<TextureHandle> LoadMaterialTextures(aiMaterial *mat, int32 textureType, const aiScene *scene, const string& sourceFile); private: std::vector<Mesh> m_meshes; }; // model.cpp Model::Model(const string& sourceFile, const string& data, const ulib::Bitset& flags) : Resource(flags, "Model", WZ_EXTENSION_MODEL) { WZ_CORE_TRACE("Reading model with assimp from data"); Assimp::Importer _importer; std::vector<Material*> _materials; std::vector<string> _meshNames; const aiScene *_scene = _importer.ReadFileFromMemory(&data[0], data.size(), aiProcess_Triangulate | aiProcess_FlipUVs | aiProcess_JoinIdenticalVertices | aiProcess_SortByPType | aiProcess_GenSmoothNormals | aiProcess_CalcTangentSpace | aiProcess_RemoveRedundantMaterials | aiProcess_OptimizeMeshes | aiProcess_OptimizeGraph ); string _sceneError = ""; if (!_scene) { WZ_CORE_ERROR("Failed loading model, Assimp error: {0}", _importer.GetErrorString()); m_isValid = false; return; } WZ_CORE_INFO("Successfully read model from data"); WZ_CORE_TRACE("Verifying scene..."); if (!VerifyAssimpScene(_scene, &_sceneError)) { WZ_CORE_ERROR("Failed loading model from data: '{0}'...", _sceneError); m_isValid = false; return; } WZ_CORE_INFO("Scene was verified successfully"); ProcessNode(_scene->mRootNode, _scene, sourceFile); m_isValid = true; WZ_CORE_INFO("Model successfully initialized"); } string Model::Serialize() const { WZ_CORE_ERROR("Model serializing not yet implemented"); return ""; } bool Model::VerifyAssimpScene(const aiScene *scene, string *error) { *error = "Unknown error"; if (!scene) { *error = "Scene was null, assimp error..."; return false; } if (!scene->HasMeshes()) { *error = "File has no meshes"; return false; } return true; } bool Model::VerifyAssimpMesh(const aiMesh *mesh, string *error) { *error = "Unknown error"; if (!mesh) { *error = "Mesh is null"; return false; } if (!mesh->HasFaces()) { *error = "Mesh has no indices"; return false; } if (!mesh->HasNormals()) { *error = "Mesh has no normals"; return false; } if (!mesh->HasPositions()) { *error = "Mesh has no positions"; return false; } return true; } bool Model::ProcessNode(aiNode *node, const aiScene *scene, const string& sourceFile) { WZ_CORE_TRACE("Processing ai node '{0}'...", node->mName.C_Str()); WZ_CORE_TRACE("Processing {0} meshes...", node->mNumMeshes); m_meshes.reserve(node->mNumMeshes); for (u32 i = 0; i < node->mNumMeshes; i++) { WZ_CORE_TRACE("Processing mesh {0}/{1}", i + 1, node->mNumMeshes); aiMesh *_mesh = scene->mMeshes[node->mMeshes[i]]; bool _meshSuccess = false; auto _processedMesh = ProcessMesh(_mesh, scene, &_meshSuccess, sourceFile, i); if (_meshSuccess) { m_meshes.push_back(_processedMesh); } else { WZ_CORE_ERROR("Failed processing node, error when processing mesh."); return false; } WZ_CORE_INFO("Successfully processed mesh {0}/{1}", i + 1, node->mNumMeshes); } WZ_CORE_TRACE("Processing {0} node children...", node->mNumChildren); bool _anyFailure = false; for (u32 i = 0; i < node->mNumChildren; i++) { WZ_CORE_TRACE("Processing child {0}/{1}", i + 1, node->mNumChildren); if (!ProcessNode(node->mChildren[i], scene, sourceFile)) { _anyFailure = true; break; } } if (_anyFailure) { return false; } WZ_CORE_INFO("Successfully processed ai node '{0}'", node->mName.C_Str()); return true; } Mesh Model::ProcessMesh(aiMesh *mesh, const aiScene *scene, bool *success, const string& sourceFile, int32 meshIndex) { *success = true; string meshError = ""; std::vector<Vertex> _vertices; std::vector<u32> _indices; WZ_CORE_TRACE("Verifying mesh"); if (!VerifyAssimpMesh(mesh, &meshError)) { WZ_CORE_ERROR("Failed processing mesh: {0}", meshError); *success = false; return Mesh(); } WZ_CORE_TRACE("Mesh OK"); WZ_CORE_TRACE("Reserving memory for {0} vertices and {1} faces ({2} indices) [{2} Bytes]", mesh->mNumVertices, mesh->mNumFaces, mesh->mNumFaces * 3, (sizeof(Vertex) * mesh->mNumVertices + sizeof(u32) * mesh->mNumFaces * 3)); _vertices.reserve(mesh->mNumVertices); _indices.reserve(mesh->mNumFaces * 3); WZ_CORE_TRACE("Iterating {0} vertices...", mesh->mNumVertices); for (u32 i = 0; i < mesh->mNumVertices; i++) { if (rand() % 500 == 3) { WZ_CORE_TRACE("Vertices progress: {0}%", (int)(((float)i / (float)mesh->mNumVertices) * 100)); } aiVector3D& _pos = mesh->mVertices[i]; aiVector3D _uv; aiVector3D& _normal = mesh->mNormals[i]; if (mesh->HasTextureCoords(0)) { _uv = mesh->mTextureCoords[0][i]; } else { _uv = aiVector3D(0, 0, 0); } _vertices.push_back({ vec3(_pos.x, _pos.y, _pos.z), vec2(_uv.x, _uv.y), vec3(_normal.x, _normal.y, _normal.z) }); } for (u32 i = 0; i < mesh->mNumFaces; i++) { aiFace& _face = mesh->mFaces[i]; WZ_CORE_ASSERT(_face.mNumIndices == 3, "Model must be triangulated (Should be done automatically at import?)"); for (u32 j = 0; j < _face.mNumIndices; j++) { _indices.push_back(_face.mIndices[j]); } } aiMaterial *_aiMat = scene->mMaterials[mesh->mMaterialIndex]; string _meshName = ulib::File::name_of(sourceFile) + "_mesh_" + std::to_string(meshIndex) + string("_") + mesh->mName.C_Str(); auto _material = ProcessMaterial(_aiMat, _meshName, scene, sourceFile); if (_material == WZ_NULL_RESOURCE_HANDLE) { WZ_CORE_ERROR("Failed processing mesh, couldn't create material"); *success = false; return Mesh(); } return { _vertices, _indices, _material, _meshName }; } MaterialHandle Model::ProcessMaterial(aiMaterial *mat, const string& meshName, const aiScene *scene, const string& sourceFile) { string _materialHandle = meshName + "_material"; Material *_material = new Material(WZ_DEFAULT_SHADER_HANDLE); auto _diffuseTextures = LoadMaterialTextures(mat, (int32)aiTextureType_DIFFUSE, scene, sourceFile); if (_diffuseTextures.size() == 0) { return WZ_NULL_RESOURCE_HANDLE; } auto _mainDiffuseTexture = _diffuseTextures[0]; _material->diffuseMapHandle = _mainDiffuseTexture; ResourceManagement::Add(_material, _materialHandle); return _materialHandle; } std::vector<TextureHandle> Model::LoadMaterialTextures(aiMaterial *mat, int32 textureType, const aiScene *scene, const string& sourceFile) { WZ_CORE_TRACE("Handling textures of texture type {0} for material...", textureType); std::vector<TextureHandle> _textures; u32 _count = mat->GetTextureCount((aiTextureType)textureType); string _fileDirectory = ulib::File::directory_of(sourceFile); string _fileName = ulib::File::without_extension(ulib::File::name_of(sourceFile)); if (_count == 0) { WZ_CORE_TRACE("No texture found in model file..."); WZ_CORE_TRACE("Searching for common disk textures in model directory..."); static string _fmts[] = { ".jpg", ".jpeg", ".png", ".tga", ".bmp", ".psd", ".gid", ".hdr", ".pic" }; // TODO string _diffuseFile = _fileDirectory + "/diffuse"; bool _foundDiffuse = false; for (const auto& _fmt : _fmts) { if (ulib::File::exists(_diffuseFile + _fmt)) { WZ_CORE_TRACE("Found '{0}'", _diffuseFile + _fmt); _diffuseFile += _fmt; _foundDiffuse = true; break; } } if (_foundDiffuse) { string _handle = _fileName + "_" + std::to_string(textureType) + "_" + ulib::File::without_extension(ulib::File::name_of(_diffuseFile)); ResourceManagement::Load<Texture>(_diffuseFile, _handle); _textures.push_back(_handle); } else { aiColor3D _aiColor(0.0f, 0.0f, 0.0f); switch ((aiTextureType)textureType) { case aiTextureType_DIFFUSE: mat->Get(AI_MATKEY_COLOR_DIFFUSE, _aiColor); break; default: WZ_CORE_ASSERT(false, "Unimplemented aiTextureType"); break; } if (_aiColor.IsBlack()) { WZ_CORE_TRACE("No color property found, using unloaded texture..."); _textures.push_back(Texture::UnloadedTexture()); } else { WZ_CORE_TRACE("Color property found, creating 1x1 texture..."); auto _color = Color(_aiColor.r, _aiColor.g, _aiColor.b, 1.f); Texture *_texture = Texture::Create((byte*)&_color, 1, 1); auto _handle = ulib::File::name_of(sourceFile) + "_" + std::to_string(textureType) + "_1x1_texture"; ResourceManagement::Add(_texture, _handle); _textures.push_back(_handle); } } } else { WZ_CORE_TRACE("{0} Textures found...", _count); _textures.reserve(_count); for (u32 i = 0; i < _count; i++) { aiString _path; mat->GetTexture((aiTextureType)textureType, i ,&_path); string _file = ulib::File::directory_of(sourceFile) + "/" + ulib::File::name_of(ulib::File::to_portable_path(_path.C_Str())); string _handle = ulib::File::name_of(sourceFile) + "_" + std::to_string(textureType) + "_" + ulib::File::name_of(_file); WZ_CORE_ASSERT(ulib::File::exists(_file), "Embedded textures not yet implemnted"); ResourceManagement::Load<Texture>(_file, _handle); _textures.push_back(_handle); } } if (_textures.size() == 0) { WZ_CORE_ERROR("Something went wrong when processing textures, applying invalid texture"); _textures.push_back(Texture::InvalidTexture()); } return _textures; }  
  • Advertisement

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!