Jump to content

klepto2

Developers
  • Posts

    857
  • Joined

  • Last visited

Posts posted by klepto2

  1. The Leadwerks 2 Editor was designed to run scripts directly, while this came in handy for things like additions it was also dangerous. Scripts with errors could have easily break the editor. In le3 and later this was leaved out, but in ultraengine I would assume that it will be possible again ( not with lua, but with direct plugins).

    even if it was cut of there are some ways to have interactive scripts. While normal lua scripts are not executed, those for posteffects are. Unfortunately the naming is a of an entity is not accessible in the editor, at least if I remember correctly. But in theory you could access all items in the loaded map and generate whatever you want. Of course you need to remove the posteffect later for the release.

  2. nevermind, got it

    viewInfo.subresourceRange.baseMipLevel = mipLevel;
    	viewInfo.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS;
    	viewInfo.subresourceRange.baseArrayLayer = 0;
    	viewInfo.subresourceRange.layerCount = texture->CountFaces();

     

    • Upvote 1
  3. Maybe just an idea:

    to hide the VkTexture things and maybe everything related to transfer data from your vulkan renderer to 3rd party code from the common user, you could add a new namespace: UltraEngine::Transfer

    in this you could have something like: VkTexture GetTransferDataForTexture(shared_ptr<Texture> texture);

    this could be added as pure functions or in a static class (which i would prefer) the benefit is that you don't need answer questions to everyone who looks into the texture class, but also everyone who needs access to it has a central point where to look at.

    2 minutes ago, Josh said:

    Have you thought about creating your own imageviews that contain the exact data you want? The engine has a limited number of image slots and if I create different versions of each mipmap level, those numbers start getting very big. There's also an issue that another library or feature in my engine might need them to be slightly different again.

    This is how the base image view is created:

    
    	bool RenderTexture::CreateImageView()
    	{
    		viewInfo = {};
    		viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
    		viewInfo.image = vkimage;
    		switch (type)
    		{
    			//case TEXTURE_1D:
    			//	viewInfo.viewType = VK_IMAGE_VIEW_TYPE_1D;
    			//	break;
    		case TEXTURE_2D:
    			viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
    			break;
    		case TEXTURE_3D:
    			viewInfo.viewType = VK_IMAGE_VIEW_TYPE_3D;
    			break;
    		case TEXTURE_CUBE:
    			viewInfo.viewType = VK_IMAGE_VIEW_TYPE_CUBE;
    			if (isshadow) viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
    			break;
    		}
    		viewInfo.format = imageInfo.format;
    		switch (format)
    		{
    		case VK_FORMAT_D24_UNORM_S8_UINT:
    		case VK_FORMAT_D32_SFLOAT:
    		case VK_FORMAT_D32_SFLOAT_S8_UINT:
    		case VK_FORMAT_D16_UNORM:
    		case VK_FORMAT_D16_UNORM_S8_UINT:
    			viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
    			break;
    		default:
    			viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
    			break;
    		}
    		viewInfo.subresourceRange.baseMipLevel = 0;
    		viewInfo.subresourceRange.levelCount = miplevels;
    		viewInfo.subresourceRange.baseArrayLayer = 0;
    		viewInfo.subresourceRange.layerCount = faces;
    		viewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
    		viewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
    		viewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
    		viewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
    
    		VkAssert(vkCreateImageView(device->device, &viewInfo, NULL, &imageview));
    		return true;
    	}

    I don't know how this would break the image slots? I would guess the way your currently doing it will brake the image slots much faster ;)

    Lets take a cubemap with 1024*1024 and the assumed 6 layers: while your appoach currently generates 66 imageviews for this, the correct size would be 10. 

    So adding the last 10 wouldn't be that hard ;)

    also the vulkan spec states that there is no limit for imageviews, though multiple people stated that theyx tested it and it breaks after around 520k but that should be more than enough ;).

  4. On 9/26/2022 at 7:55 AM, klepto2 said:

    this is what i use to create the IBL map from the sky-cubemap:

    
    #version 450
    
    #extension GL_ARB_separate_shader_objects : enable
    #extension GL_ARB_shading_language_420pack : enable
    
    layout (local_size_x = 16, local_size_y = 16, local_size_z = 1) in;
    
    layout (set = 0, binding = 0) uniform samplerCube envSkybox;
    layout (set = 0, binding = 1, rgba32f) uniform imageCube envReflection;
    
    layout (push_constant) uniform Contants
    {
    	vec4 data;
    } constants;
    
    const uint numSamples = 16;
    #define PI 3.14159265359
    float roughnessLevel = constants.data.x;
    
    float RadicalInverse_VdC(uint bits)
    {
    	bits = (bits << 16u) | (bits >> 16u);
    	bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
    	bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
    	bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
    	bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
    	return float(bits) * 2.3283064365386963e-10; // / 0x100000000
    }
    
    // https://learnopengl.com/#!PBR/IBL/Specular-IBL
    vec2 Hammersley(uint i, uint N)
    {
    	return vec2(float(i) / float(N), RadicalInverse_VdC(i));
    }
    // https://learnopengl.com/#!PBR/IBL/Specular-IBL
    vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness)
    {
    	float a = roughness*roughness;
    	float phi = 2.0 * PI * Xi.x;
    	float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a*a - 1.0) * Xi.y));
    	float sinTheta = sqrt(1.0 - cosTheta*cosTheta);
    	// from spherical coordinates to cartesian coordinates
    	vec3 H;
    	H.x = cos(phi) * sinTheta;
    	H.y = sin(phi) * sinTheta;
    	H.z = cosTheta;
    	// from tangent-space vector to world-space sample vector
    	vec3 up = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
    	vec3 tangent = normalize(cross(up, N));
    	vec3 bitangent = cross(N, tangent);
    	vec3 sampleVec = tangent * H.x + bitangent * H.y + N * H.z;
    	return normalize(sampleVec);
    }
    
    vec3 cubeCoordToWorld(ivec3 cubeCoord, vec2 cubemapSize)
    {
        vec2 texCoord = vec2(cubeCoord.xy) / cubemapSize;
        texCoord = texCoord  * 2.0 - 1.0; // -1..1
    
        switch(cubeCoord.z)
        {
            case 0: return vec3(1.0, -texCoord.yx); // posx
            case 1: return vec3(-1.0, -texCoord.y, texCoord.x); //negx
            case 2: return vec3(texCoord.x, 1.0, texCoord.y); // posy
            case 3: return vec3(texCoord.x, -1.0, -texCoord.y); //negy
            case 4: return vec3(texCoord.x, -texCoord.y, 1.0); // posz
            case 5: return vec3(-texCoord.xy, -1.0); // negz
        }
        return vec3(0.0);
    }
      
    void main() 
    {
    	ivec3 cubeCoord = ivec3(gl_GlobalInvocationID);
    	vec3 viewDir = normalize(cubeCoordToWorld(cubeCoord, vec2(imageSize(envReflection))));
    
    	vec3 N = normalize(viewDir);
    	vec3 R = N;
    	vec3 V = N;
    
    	float weight = 0;
    	vec3 color = vec3(0);
    
    	for (int samples = 0; samples < numSamples; samples++)
    	{
    		vec2 Xi = Hammersley(samples, numSamples);
    		vec3 L = ImportanceSampleGGX(Xi, N, roughnessLevel); 
    
    		float NdotL = dot(N, L);
    		if (NdotL > 0)
    		{
    			color += texture(envSkybox, L).rgb;
    			weight += NdotL;
    		}
    	}
    
    	imageStore(envReflection,	
    		cubeCoord,
    		vec4(color / weight, 1.0));
    }

    on some discussions is mentioned, that you should always use computeshader when u write to cubemaps,  But from your code i don't think this is actually true, because in the discussions it is assumed, that you need to attach and render each face separatly in the fragment shader, but you just attach each face as a different target, so even if i prefer the compute way yours should be fast as well.

    i use the above shader this to create the IBL texture, and this code below is the actual cpp code to setup the mipmapgeneration per roughness:

    vector<EnvironmentSkyReflectionContants> reflShaders;
    	for (int layer = 0; layer < reflectionTexture->CountMipmaps(); layer++)
    	{
    		shared_ptr<ComputeShader> refshader;
    		int tx = 16;
    		if (reflectionTexture->GetMipmapWidth(layer) < 16)
    		{
    			refshader = ComputeShader::Create("Shaders\\Environment\\env_reflection_gen_1.comp.spv");
    			tx = 1;
    		}
    		else
    		{
    			refshader = ComputeShader::Create("Shaders\\Environment\\env_reflection_gen.comp.spv");
    		}
    		
    		refshader->AddSampler(envSkyWithoutSun);
    		refshader->AddTargetImage(reflectionTexture,layer);
    		refshader->SetupPushConstant(sizeof(EnvironmentSkyReflectionContants));
    		EnvironmentSkyReflectionContants data;
    		data.reflectiondata.x = layer / reflectionTexture->CountMipmaps();
    		data.reflectiondata.y = layer;
    		
    		refshader->BeginDispatch(world, Max(1,reflectionTexture->GetMipmapWidth(layer)) / tx, Max(1, reflectionTexture->GetMipmapHeight(layer)) / tx, 6, false, ComputeHook::RENDER, &data, sizeof(EnvironmentSkyReflectionContants));
    		reflShaders.push_back(data);
    	}

     

  5. I still get the error, but i don't get this error in this sample:

    #include "UltraEngine.h"
    #include "ComponentSystem.h"
    
    using namespace UltraEngine;
    
    int main(int argc, const char* argv[])
    {
        //Get the displays
        auto displays = GetDisplays();
    
        //Create a window
        auto window = CreateWindow("Ultra Engine", 0, 0, 1280 * displays[0]->scale, 720 * displays[0]->scale, displays[0], WINDOW_CENTER | WINDOW_TITLEBAR);
    
        //Create a world
        auto world = CreateWorld();
    
        //Create a framebuffer
        auto framebuffer = CreateFramebuffer(window);
    
        //Create a camera
        auto camera = CreateCamera(world);
        camera->SetClearColor(0.125);
        camera->SetFOV(70);
        camera->SetPosition(0, 0, -3);
        
        //Create a light
        auto light = CreateDirectionalLight(world);
        light->SetRotation(35, 45, 0);
    
        //Create a box
        auto box = CreateBox(world);
    
        //Entity component system
        auto actor = CreateActor(box);
        auto component = actor->AddComponent<Mover>();
        component->rotation.y = 45;
    
        auto envSky = CreateTexture(TEXTURE_CUBE, 1024, 1024, TEXTURE_RGBA32, {}
        , 6, TEXTURE_STORAGE, TEXTUREFILTER_LINEAR, 0);
    
        world->SetEnvironmentMap(envSky, ENVIRONMENTMAP_BACKGROUND);
        VkTexture textureData;
        //Main loop
        while (window->Closed() == false and window->KeyDown(KEY_ESCAPE) == false)
        {
            world->Update();
            world->Render(framebuffer);
            textureData = envSky->GetVKTexture();
        } //Add breakpoint here to see the broken VkTexture
        return 0;
    }

    but if you set a breakpoint add the second last }, you can see that VKTexture contains garbage.

    [Edit:] for the include problem i needed to restore sol3 and nlohman from a previously made backup.

  6. this is what i use to create the IBL map from the sky-cubemap:

    #version 450
    
    #extension GL_ARB_separate_shader_objects : enable
    #extension GL_ARB_shading_language_420pack : enable
    
    layout (local_size_x = 16, local_size_y = 16, local_size_z = 1) in;
    
    layout (set = 0, binding = 0) uniform samplerCube envSkybox;
    layout (set = 0, binding = 1, rgba32f) uniform imageCube envReflection;
    
    layout (push_constant) uniform Contants
    {
    	vec4 data;
    } constants;
    
    const uint numSamples = 16;
    #define PI 3.14159265359
    float roughnessLevel = constants.data.x;
    
    float RadicalInverse_VdC(uint bits)
    {
    	bits = (bits << 16u) | (bits >> 16u);
    	bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
    	bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
    	bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
    	bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
    	return float(bits) * 2.3283064365386963e-10; // / 0x100000000
    }
    
    // https://learnopengl.com/#!PBR/IBL/Specular-IBL
    vec2 Hammersley(uint i, uint N)
    {
    	return vec2(float(i) / float(N), RadicalInverse_VdC(i));
    }
    // https://learnopengl.com/#!PBR/IBL/Specular-IBL
    vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness)
    {
    	float a = roughness*roughness;
    	float phi = 2.0 * PI * Xi.x;
    	float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a*a - 1.0) * Xi.y));
    	float sinTheta = sqrt(1.0 - cosTheta*cosTheta);
    	// from spherical coordinates to cartesian coordinates
    	vec3 H;
    	H.x = cos(phi) * sinTheta;
    	H.y = sin(phi) * sinTheta;
    	H.z = cosTheta;
    	// from tangent-space vector to world-space sample vector
    	vec3 up = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
    	vec3 tangent = normalize(cross(up, N));
    	vec3 bitangent = cross(N, tangent);
    	vec3 sampleVec = tangent * H.x + bitangent * H.y + N * H.z;
    	return normalize(sampleVec);
    }
    
    vec3 cubeCoordToWorld(ivec3 cubeCoord, vec2 cubemapSize)
    {
        vec2 texCoord = vec2(cubeCoord.xy) / cubemapSize;
        texCoord = texCoord  * 2.0 - 1.0; // -1..1
    
        switch(cubeCoord.z)
        {
            case 0: return vec3(1.0, -texCoord.yx); // posx
            case 1: return vec3(-1.0, -texCoord.y, texCoord.x); //negx
            case 2: return vec3(texCoord.x, 1.0, texCoord.y); // posy
            case 3: return vec3(texCoord.x, -1.0, -texCoord.y); //negy
            case 4: return vec3(texCoord.x, -texCoord.y, 1.0); // posz
            case 5: return vec3(-texCoord.xy, -1.0); // negz
        }
        return vec3(0.0);
    }
      
    void main() 
    {
    	ivec3 cubeCoord = ivec3(gl_GlobalInvocationID);
    	vec3 viewDir = normalize(cubeCoordToWorld(cubeCoord, vec2(imageSize(envReflection))));
    
    	vec3 N = normalize(viewDir);
    	vec3 R = N;
    	vec3 V = N;
    
    	float weight = 0;
    	vec3 color = vec3(0);
    
    	for (int samples = 0; samples < numSamples; samples++)
    	{
    		vec2 Xi = Hammersley(samples, numSamples);
    		vec3 L = ImportanceSampleGGX(Xi, N, roughnessLevel); 
    
    		float NdotL = dot(N, L);
    		if (NdotL > 0)
    		{
    			color += texture(envSkybox, L).rgb;
    			weight += NdotL;
    		}
    	}
    
    	imageStore(envReflection,	
    		cubeCoord,
    		vec4(color / weight, 1.0));
    }

    on some discussions is mentioned, that you should always use computeshader when u write to cubemaps,  But from your code i don't think this is actually true, because in the discussions it is assumed, that you need to attach and render each face separatly in the fragment shader, but you just attach each face as a different target, so even if i prefer the compute way yours should be fast as well.

  7. From my experience, you only need the miplevels. (At least i just needed the layercount) the layer for the cubemap are accessed by the z component. In the sky rendering I always render to the full cubemap at once. Well for the clouds I currently try to implement temporal reprojection, this allows rendering of 1/16th of the whole cubemap in one frame without a noticeable lag or noise. ( the clouds in the above screenshot let the framerate drop from hundreds to just 20 fps so there is need for optimization)

×
×
  • Create New...