but all pixels have to be same.
Problem started when I tried to implement volumetric clouds (you can see noise here video). Then I just simplified code. Also original code and shader which draw volumetric clouds work fine on OpenGL.
CMake project is available here
There are steps:
-
Init vulkan.
-
Create pipeline, image and etc.
-
Call vkCmdDispatch()
-
Call vkMapMemory() to get result
VkCommandBuffer pCommandBuffer; //draw { pCommandBuffer = beginCommandBuffer(commandPool); vkCmdBindPipeline(pCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline); vkCmdBindDescriptorSets( pCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipelineLayout, 0, 1, &descriptorSet, 0, nullptr); vkCmdDispatch(pCommandBuffer, alignUp(WIDTH, (uint32)8) / 8, alignUp(HEIGHT, (uint32)8) / 8, 1); endCommandBuffer(pCommandBuffer); } //output { pCommandBuffer = beginCommandBuffer(commandPool); imageBarrier( pCommandBuffer, image, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, VK_IMAGE_LAYOUT_GENERAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_READ_BIT, VK_IMAGE_LAYOUT_GENERAL); endCommandBuffer(pCommandBuffer); void* bufData; vkMapMemory(vulkanRHI_GetDevice(), imageMemory, 0, sizeof(FPixel)*WIDTH*HEIGHT, 0, &bufData); memcpy(pixels.data(), bufData, sizeof(FPixel)*WIDTH*HEIGHT); vkUnmapMemory(vulkanRHI_GetDevice(), imageMemory); pCommandBuffer = beginCommandBuffer(commandPool); imageBarrier( pCommandBuffer, image, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_READ_BIT, VK_IMAGE_LAYOUT_GENERAL, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, VK_IMAGE_LAYOUT_GENERAL); endCommandBuffer(pCommandBuffer); fstream file; file.open(("attempt_" + to_string(i) + ".txt").c_str(), ios::out); file << "\n\n\n"; for (int x = 0; x < 300; x++) { for (int y = 0; y < 300; y++) file << (pixels[x + y * WIDTH].r > 128 ? '+' : ' '); file << "\n"; } }
Compute shader just does same computation for every pixel. What I get:
You can see random holes which have size 8x8.
Shader code:
#version 450
#extension GL_ARB_separate_shader_objects : enable
layout(local_size_x = 8, local_size_y = 8) in;
layout(rgba8, binding = 3) uniform image2D ufmResultImage;
bool crossRaySphereOut(vec3 rayStart, vec3 rayDirection, vec3 sphereCenter, float sphereRadius, out vec3 result1, out vec3 result2)
{
vec3 rayPoint=rayStart+dot(sphereCenter-rayStart, rayDirection)*rayDirection;
float height=length(rayPoint-sphereCenter);
if(height<=sphereRadius)
{
float dist=sqrt(sphereRadius*sphereRadius-height*height);
result1=rayPoint-rayDirection*dist;
result2=rayPoint+rayDirection*dist;
return dot(result1-rayStart,rayDirection)>0.0f || dot(result2-rayStart,rayDirection)>0.0f;
}
return false;
}
bool crossRaySphereOutFar(vec3 rayStart, vec3 rayDirection, vec3 sphereCenter, float sphereRadius, out vec3 result)
{
vec3 tmpPoint;
return crossRaySphereOut(rayStart, rayDirection, sphereCenter, sphereRadius, tmpPoint, result);
}
float remap(float value, float minValue, float maxValue, float newMinValue, float newMaxValue)
{
return newMinValue+(value-minValue)/(maxValue-minValue)*(newMaxValue-newMinValue);
}
float cloudGetStepLength(vec3 position, float avrStep)
{
return avrStep;
}
float cloudGetHeight(vec3 position)
{
return clamp((length(position)-(6400+15))/((6400+35)-(6400+15)), 0, 1);
}
float cloudSampleDensity(vec3 position, float mip, bool fast)
{
float gc=1;
vec4 weather=vec4(1);
float wc0=weather.r;
float wc1=weather.g;
float wh=weather.b;
float wd=weather.a;
float ph=cloudGetHeight(position);
float WMc=max(wc0, clamp(gc-0.5, 0, 1)*wc1*2);
float SRb=clamp(remap(ph, 0, 0.07, 0, 1), 0, 1);
float SRt=clamp(remap(ph, wh*0.2, wh, 1, 0), 0, 1);
float SA=SRb*SRt;
float DRb=ph*clamp(remap(ph, 0, 0.15, 0, 1), 0, 1);
float DRt=ph*clamp(remap(ph, 0.9, 1, 1, 0), 0, 1);
float DA=DRb*DRt*wd*2;
float SNsample=1;
float d;
if(fast)
{
d=clamp(remap(SNsample*SA, 1-gc*WMc, 1, 0, 1), 0, 1)*DA;
}
else
{
float SNnd=clamp(remap(SNsample*SA, 1-gc*WMc, 1, 0, 1), 0, 1);
float DNfbm = 1;
float DNmod=0.35*exp(-gc*0.75)*mix(DNfbm, 1-DNfbm, clamp(ph*5, 0, 1));
d=clamp(remap(SNnd, DNmod, 1, 0, 1)*DA, 0, 1);
}
return d;
}
float cloudSampleSunDensity(vec3 position, vec3 sunDir, float mip)
{
float avrStep=((6400+35)-(6400+15));
float sumDensity=0;
float prevDensity=0;
for(int i=0;i<6;i++)
{
float step=cloudGetStepLength(position, avrStep);
position+=sunDir*step;
float density=cloudSampleDensity(position, mip, false);
float actualDensity=(density+prevDensity)*0.5;
prevDensity=density;
sumDensity+=actualDensity*step;
}
return sumDensity;
}
vec4 mainMarching(vec3 viewDir, vec3 sunDir, vec3 sunColor)
{
vec3 position;
crossRaySphereOutFar(vec3(0, 6400, 0), viewDir, vec3(0), (6400+15), position);
float avrStep=((6400+35)-(6400+15))/float(3/2);
position=position+viewDir*cloudGetStepLength(position, avrStep);
vec3 color=vec3(0);
float transmittance=1;
int zeroSamplesCount=0;
float sampleTest=0;
float step;
float prevDensity=0;
for(int i=0;i<160;i++)
{
if(sampleTest>0)
{
float density=cloudSampleDensity(position, 0, false);
float actualDensity=(density+prevDensity)*0.5;
prevDensity=density;
if(actualDensity>0)
{
float sunDensity=cloudSampleSunDensity(position, sunDir, 0);
vec3 light=15*sunColor*exp(-4*sunDensity)*(1-exp(-4*actualDensity*step));
light*=4*actualDensity;
color+=light*transmittance;
}
else
{
zeroSamplesCount++;
}
if(zeroSamplesCount<6)
{
step=cloudGetStepLength(position, avrStep);
position+=viewDir*step;
}
else
{
zeroSamplesCount=0;
sampleTest=0;
}
}
else
{
sampleTest=cloudSampleDensity(position, 0, true);
if(sampleTest<=0)
{
step=cloudGetStepLength(position, avrStep);
position+=viewDir*step;
}
}
if(transmittance<0.00001 || length(position)>(6400+35))
break;
}
return vec4(color*10, 1);
}
void main()
{
ivec2 g_id = ivec2(gl_GlobalInvocationID.x, gl_GlobalInvocationID.y);
if (g_id.x>=0 && g_id.x < 300 && g_id.y>=0 && g_id.y < 300)
{
vec3 viewDir=normalize(vec3(1,1,1));
vec3 sunDir= normalize(vec3(1,1,1));
vec3 sunColor=vec3(1,1,1);
imageStore(ufmResultImage, g_id, mainMarching(viewDir, sunDir, sunColor));
}
}
I have no idea what is going on. Looks like shader little bit complicated and somehow it gets access to wrong memory.