/// <summary> /// Read a KtxFile and decode it. /// </summary> public Image <Rgba32> Decode(KtxFile file) { if (IsSupportedRawFormat(file.Header.GlInternalFormat)) { var decoder = GetRawDecoder(file.Header.GlInternalFormat); var data = file.MipMaps[0].Faces[0].Data; var pixelWidth = file.MipMaps[0].Width; var pixelHeight = file.MipMaps[0].Height; var image = new Image <Rgba32>((int)pixelWidth, (int)pixelHeight); var output = decoder.Decode(data, (int)pixelWidth, (int)pixelHeight); var pixels = image.GetPixelSpan(); output.CopyTo(pixels); return(image); } else { var decoder = GetDecoder(file.Header.GlInternalFormat); if (decoder == null) { throw new NotSupportedException($"This format is not supported: {file.Header.GlInternalFormat}"); } var data = file.MipMaps[0].Faces[0].Data; var pixelWidth = file.MipMaps[0].Width; var pixelHeight = file.MipMaps[0].Height; var blocks = decoder.Decode(data, (int)pixelWidth, (int)pixelHeight, out var blockWidth, out var blockHeight); return(ImageToBlocks.ImageFromRawBlocks(blocks, blockWidth, blockHeight, (int)pixelWidth, (int)pixelHeight)); } }
public static float DecodeCheckPSNR(string filename, Image <Rgba32> original) { using FileStream fs = File.OpenRead(filename); var ktx = KtxFile.Load(fs); var decoder = new BcDecoder(); using var img = decoder.Decode(ktx); var pixels = original.GetPixelSpan(); var pixels2 = img.GetPixelSpan(); return(ImageQuality.PeakSignalToNoiseRatio(pixels, pixels2, true)); }
/// <summary> /// Encodes all mipmap levels into a ktx or a dds file and writes it to the output stream. /// </summary> public void Encode(Image <Rgba32> inputImage, Stream outputStream) { if (OutputOptions.fileFormat == OutputFileFormat.Ktx) { KtxFile output = EncodeToKtx(inputImage); output.Write(outputStream); } else if (OutputOptions.fileFormat == OutputFileFormat.Dds) { DdsFile output = EncodeToDds(inputImage); output.Write(outputStream); } }
/// <summary> /// Encodes all cubemap faces and mipmap levels into Ktx file and writes it to the output stream. /// Order is +X, -X, +Y, -Y, +Z, -Z /// </summary> public void EncodeCubeMap(Image <Rgba32> right, Image <Rgba32> left, Image <Rgba32> top, Image <Rgba32> down, Image <Rgba32> back, Image <Rgba32> front, Stream outputStream) { if (OutputOptions.fileFormat == OutputFileFormat.Ktx) { KtxFile output = EncodeCubeMapToKtx(right, left, top, down, back, front); output.Write(outputStream); } else if (OutputOptions.fileFormat == OutputFileFormat.Dds) { DdsFile output = EncodeCubeMapToDds(right, left, top, down, back, front); output.Write(outputStream); } }
public ImageInfo Load(Stream input) { // Load Ktx file var ktxFile = KtxFile.Load(input); // Prepare main image data var imageData = ktxFile.MipMaps[0].Faces[0].Data; var imageFormat = (int)ktxFile.header.GlInternalFormat; var size = new Size((int)ktxFile.header.PixelWidth, (int)ktxFile.header.PixelHeight); // Prepare mip maps return(new ImageInfo(imageData, imageFormat, size) { MipMapData = ktxFile.MipMaps.Skip(1).Select(x => x.Faces[0].Data).ToArray() }); }
/// <summary> /// Read a Ktx or a Dds file from a stream and decode it. /// </summary> public Image <Rgba32>[] DecodeAllMipMaps(Stream inputStream) { var position = inputStream.Position; try { if (inputStream is FileStream fs) { var extension = Path.GetExtension(fs.Name).ToLower(); if (extension == ".ktx") { KtxFile file = KtxFile.Load(inputStream); return(DecodeAllMipMaps(file)); } else if (extension == ".dds") { DdsFile file = DdsFile.Load(inputStream); return(DecodeAllMipMaps(file)); } } bool isDDS = false; using (var br = new BinaryReader(inputStream, Encoding.UTF8, true)) { var magic = br.ReadUInt32(); if (magic == 0x20534444U) { isDDS = true; } } inputStream.Seek(position, SeekOrigin.Begin); if (isDDS) { DdsFile dds = DdsFile.Load(inputStream); return(DecodeAllMipMaps(dds)); } else { KtxFile ktx = KtxFile.Load(inputStream); return(DecodeAllMipMaps(ktx)); } } catch (Exception) { inputStream.Seek(position, SeekOrigin.Begin); throw; } }
public void Save(Stream output, ImageInfo imageInfo) { // Create Ktx file var ktxHeader = CreateKtxHeader(imageInfo.ImageFormat, imageInfo.ImageSize); var ktxFile = new KtxFile(ktxHeader); // Add main image to Ktx ktxFile.MipMaps.Add(CreateMipMap(imageInfo.ImageData, imageInfo.ImageSize)); // Add mips to Ktx for (var i = 1; i <= imageInfo.MipMapCount; i++) { ktxFile.MipMaps.Add(CreateMipMap(imageInfo.MipMapData[i - 1], new Size(imageInfo.ImageSize.Width >> i, imageInfo.ImageSize.Height >> i))); } ktxFile.Write(output); }
public void Bc4Decode() { using FileStream fs = File.OpenRead(@"../../../testImages/test_decompress_bc4_unorm.ktx"); KtxFile file = KtxFile.Load(fs); Assert.True(file.Header.VerifyHeader()); Assert.Equal((uint)1, file.Header.NumberOfFaces); BcDecoder decoder = new BcDecoder(); using var image = decoder.Decode(file); Assert.Equal((uint)image.Width, file.Header.PixelWidth); Assert.Equal((uint)image.Height, file.Header.PixelHeight); using FileStream outFs = File.OpenWrite("decoding_test_bc4.png"); image.SaveAsPng(outFs); }
/// <summary> /// Encodes all mipmap levels into a Ktx file. /// </summary> public KtxFile EncodeToKtx(Image <Rgba32> inputImage) { KtxFile output; IBcBlockEncoder compressedEncoder = null; IRawEncoder uncompressedEncoder = null; if (OutputOptions.format.IsCompressedFormat()) { compressedEncoder = GetEncoder(OutputOptions.format); if (compressedEncoder == null) { throw new NotSupportedException($"This format is not supported: {OutputOptions.format}"); } output = new KtxFile( KtxHeader.InitializeCompressed(inputImage.Width, inputImage.Height, compressedEncoder.GetInternalFormat(), compressedEncoder.GetBaseInternalFormat())); } else { uncompressedEncoder = GetRawEncoder(OutputOptions.format); output = new KtxFile( KtxHeader.InitializeUncompressed(inputImage.Width, inputImage.Height, uncompressedEncoder.GetGlType(), uncompressedEncoder.GetGlFormat(), uncompressedEncoder.GetGlTypeSize(), uncompressedEncoder.GetInternalFormat(), uncompressedEncoder.GetBaseInternalFormat())); } uint numMipMaps = (uint)OutputOptions.maxMipMapLevel; if (!OutputOptions.generateMipMaps) { numMipMaps = 1; } var mipChain = MipMapper.GenerateMipChain(inputImage, ref numMipMaps); for (int i = 0; i < numMipMaps; i++) { byte[] encoded = null; if (OutputOptions.format.IsCompressedFormat()) { var blocks = ImageToBlocks.ImageTo4X4(mipChain[i].Frames[0], out int blocksWidth, out int blocksHeight); encoded = compressedEncoder.Encode(blocks, blocksWidth, blocksHeight, OutputOptions.quality, !Debugger.IsAttached && Options.multiThreaded); } else { encoded = uncompressedEncoder.Encode(mipChain[i].GetPixelSpan()); } output.MipMaps.Add(new KtxMipmap((uint)encoded.Length, (uint)inputImage.Width, (uint)inputImage.Height, 1)); output.MipMaps[i].Faces[0] = new KtxMipFace(encoded, (uint)inputImage.Width, (uint)inputImage.Height); } foreach (var image in mipChain) { image.Dispose(); } output.Header.NumberOfFaces = 1; output.Header.NumberOfMipmapLevels = numMipMaps; return(output); }
protected override void CreateResources(ResourceFactory factory) { _vertexLayout = new VertexLayoutDescription( new VertexElementDescription("Position", VertexElementSemantic.Position, VertexElementFormat.Float3), new VertexElementDescription("UV", VertexElementSemantic.TextureCoordinate, VertexElementFormat.Float2), new VertexElementDescription("Color", VertexElementSemantic.Color, VertexElementFormat.Float3), new VertexElementDescription("Normal", VertexElementSemantic.Normal, VertexElementFormat.Float3)); using (Stream planeModelStream = OpenEmbeddedAssetStream("plane2.dae")) { _planeModel = new Model( GraphicsDevice, factory, planeModelStream, "dae", _vertexLayout, new Model.ModelCreateInfo(new Vector3(0.5f, 0.5f, 0.5f), Vector2.One, Vector3.Zero)); } using (Stream dragonModelStream = OpenEmbeddedAssetStream("chinesedragon.dae")) { _dragonModel = new Model( GraphicsDevice, factory, dragonModelStream, "dae", _vertexLayout, new Model.ModelCreateInfo(new Vector3(0.3f, -0.3f, 0.3f), Vector2.One, Vector3.Zero)); } using (Stream colorMapStream = OpenEmbeddedAssetStream("darkmetal_bc3_unorm.ktx")) { _colorMap = KtxFile.LoadTexture( GraphicsDevice, factory, colorMapStream, PixelFormat.BC3_UNorm); } _colorView = factory.CreateTextureView(_colorMap); _offscreenColor = factory.CreateTexture(TextureDescription.Texture2D( OffscreenWidth, OffscreenHeight, 1, 1, PixelFormat.R8_G8_B8_A8_UNorm, TextureUsage.RenderTarget | TextureUsage.Sampled)); _offscreenView = factory.CreateTextureView(_offscreenColor); Texture offscreenDepth = factory.CreateTexture(TextureDescription.Texture2D( OffscreenWidth, OffscreenHeight, 1, 1, PixelFormat.R16_UNorm, TextureUsage.DepthStencil)); _offscreenFB = factory.CreateFramebuffer(new FramebufferDescription(offscreenDepth, _offscreenColor)); ShaderSetDescription phongShaders = new ShaderSetDescription( new[] { _vertexLayout }, new[] { LoadShader(factory, "Phong", ShaderStages.Vertex, "VS"), LoadShader(factory, "Phong", ShaderStages.Fragment, "FS") }); ResourceLayout phongLayout = factory.CreateResourceLayout(new ResourceLayoutDescription( new ResourceLayoutElementDescription("UBO", ResourceKind.UniformBuffer, ShaderStages.Vertex))); GraphicsPipelineDescription pd = new GraphicsPipelineDescription( BlendStateDescription.SingleOverrideBlend, DepthStencilStateDescription.DepthOnlyLessEqual, new RasterizerStateDescription(FaceCullMode.Front, PolygonFillMode.Solid, FrontFace.Clockwise, true, false), PrimitiveTopology.TriangleList, phongShaders, phongLayout, _offscreenFB.OutputDescription); _offscreenPipeline = factory.CreateGraphicsPipeline(pd); pd.Outputs = GraphicsDevice.SwapchainFramebuffer.OutputDescription; pd.RasterizerState = RasterizerStateDescription.Default; _dragonPipeline = factory.CreateGraphicsPipeline(pd); ResourceLayout mirrorLayout = factory.CreateResourceLayout(new ResourceLayoutDescription( new ResourceLayoutElementDescription("UBO", ResourceKind.UniformBuffer, ShaderStages.Vertex), new ResourceLayoutElementDescription("ReflectionMap", ResourceKind.TextureReadOnly, ShaderStages.Fragment), new ResourceLayoutElementDescription("ReflectionMapSampler", ResourceKind.Sampler, ShaderStages.Fragment), new ResourceLayoutElementDescription("ColorMap", ResourceKind.TextureReadOnly, ShaderStages.Fragment), new ResourceLayoutElementDescription("ColorMapSampler", ResourceKind.Sampler, ShaderStages.Fragment))); ShaderSetDescription mirrorShaders = new ShaderSetDescription( new[] { _vertexLayout }, new[] { LoadShader(factory, "Mirror", ShaderStages.Vertex, "VS"), LoadShader(factory, "Mirror", ShaderStages.Fragment, "FS") }); GraphicsPipelineDescription mirrorPD = new GraphicsPipelineDescription( BlendStateDescription.SingleOverrideBlend, DepthStencilStateDescription.DepthOnlyLessEqual, new RasterizerStateDescription(FaceCullMode.None, PolygonFillMode.Solid, FrontFace.Clockwise, true, false), PrimitiveTopology.TriangleList, mirrorShaders, mirrorLayout, GraphicsDevice.SwapchainFramebuffer.OutputDescription); _mirrorPipeline = factory.CreateGraphicsPipeline(ref mirrorPD); _uniformBuffers_vsShared = factory.CreateBuffer(new BufferDescription(208, BufferUsage.UniformBuffer | BufferUsage.Dynamic)); _uniformBuffers_vsMirror = factory.CreateBuffer(new BufferDescription(208, BufferUsage.UniformBuffer | BufferUsage.Dynamic)); _uniformBuffers_vsOffScreen = factory.CreateBuffer(new BufferDescription(208, BufferUsage.UniformBuffer | BufferUsage.Dynamic)); _offscreenResourceSet = factory.CreateResourceSet(new ResourceSetDescription(phongLayout, _uniformBuffers_vsOffScreen)); _dragonResourceSet = factory.CreateResourceSet(new ResourceSetDescription(phongLayout, _uniformBuffers_vsShared)); _mirrorResourceSet = factory.CreateResourceSet(new ResourceSetDescription(mirrorLayout, _uniformBuffers_vsMirror, _offscreenView, GraphicsDevice.LinearSampler, _colorView, GraphicsDevice.Aniso4xSampler)); _cl = factory.CreateCommandList(); }
public void LoadFromStream(Stream file) { tex2D = KtxFile.Load(file, false); }
private void TransferDataKtx(KtxFile tex2D) { using CommandBuffer copyCmd = device.GetCommandPool().Rent(); VkMemoryAllocateInfo memAllocInfo = Initializers.memoryAllocateInfo(); VkMemoryRequirements memReqs; copyCmd.Begin(); // Create a host-visible staging buffer that contains the raw image data VkBuffer stagingBuffer; VkDeviceMemory stagingMemory; VkBufferCreateInfo bufferCreateInfo = Initializers.bufferCreateInfo(); bufferCreateInfo.size = tex2D.GetTotalSize(); // This buffer is used as a transfer source for the buffer copy bufferCreateInfo.usage = VkBufferUsageFlags.TransferSrc; bufferCreateInfo.sharingMode = VkSharingMode.Exclusive; Util.CheckResult(vkCreateBuffer(device.device, &bufferCreateInfo, null, &stagingBuffer)); // Get memory requirements for the staging buffer (alignment, memory type bits) vkGetBufferMemoryRequirements(device.device, stagingBuffer, &memReqs); memAllocInfo.allocationSize = memReqs.size; // Get memory type index for a host visible buffer memAllocInfo.memoryTypeIndex = device.vulkanDevice.getMemoryType(memReqs.memoryTypeBits, VkMemoryPropertyFlags.HostVisible | VkMemoryPropertyFlags.HostCoherent); Util.CheckResult(vkAllocateMemory(device.device, &memAllocInfo, null, &stagingMemory)); Util.CheckResult(vkBindBufferMemory(device.device, stagingBuffer, stagingMemory, 0)); // Copy texture data into staging buffer byte *data; Util.CheckResult(vkMapMemory(device.device, stagingMemory, 0, memReqs.size, 0, (void **)&data)); byte[] pixelData = tex2D.GetAllTextureData(); fixed(byte *pixelDataPtr = &pixelData[0]) { Unsafe.CopyBlock(data, pixelDataPtr, (uint)pixelData.Length); } vkUnmapMemory(device.device, stagingMemory); // Setup buffer copy regions for each mip level using NativeList <VkBufferImageCopy> bufferCopyRegions = new NativeList <VkBufferImageCopy>(); uint offset = 0; for (uint i = 0; i < mipLevels; i++) { VkBufferImageCopy bufferCopyRegion = new VkBufferImageCopy(); bufferCopyRegion.imageSubresource.aspectMask = VkImageAspectFlags.Color; bufferCopyRegion.imageSubresource.mipLevel = i; bufferCopyRegion.imageSubresource.baseArrayLayer = 0; bufferCopyRegion.imageSubresource.layerCount = 1; bufferCopyRegion.imageExtent.width = tex2D.Faces[0].Mipmaps[i].Width; bufferCopyRegion.imageExtent.height = tex2D.Faces[0].Mipmaps[i].Height; bufferCopyRegion.imageExtent.depth = 1; bufferCopyRegion.bufferOffset = offset; bufferCopyRegions.Add(bufferCopyRegion); offset += tex2D.Faces[0].Mipmaps[i].SizeInBytes; } VkImageSubresourceRange subresourceRange = new VkImageSubresourceRange(); subresourceRange.aspectMask = VkImageAspectFlags.Color; subresourceRange.baseMipLevel = 0; subresourceRange.levelCount = mipLevels; subresourceRange.layerCount = 1; // Image barrier for optimal image (target) // Optimal image will be used as destination for the copy Tools.setImageLayout( copyCmd.vkCmd, image, VkImageAspectFlags.Color, VkImageLayout.Undefined, VkImageLayout.TransferDstOptimal, subresourceRange); // Copy mip levels from staging buffer vkCmdCopyBufferToImage( copyCmd.vkCmd, stagingBuffer, image, VkImageLayout.TransferDstOptimal, bufferCopyRegions.Count, bufferCopyRegions.Data); // Change texture image layout to shader read after all mip levels have been copied Tools.setImageLayout( copyCmd.vkCmd, image, VkImageAspectFlags.Color, VkImageLayout.TransferDstOptimal, imageLayout, subresourceRange); copyCmd.End(); device.FlushCommandBuffer(copyCmd); //device.flushCommandBuffer(copyCmd, copyQueue); // Clean up staging resources vkFreeMemory(device.device, stagingMemory, null); vkDestroyBuffer(device.device, stagingBuffer, null); }
protected override void CreateResources(ResourceFactory factory) { _projectionBuffer = factory.CreateBuffer(new BufferDescription(64, BufferUsage.UniformBuffer | BufferUsage.Dynamic)); _viewBuffer = factory.CreateBuffer(new BufferDescription(64, BufferUsage.UniformBuffer | BufferUsage.Dynamic)); _worldBuffer = factory.CreateBuffer(new BufferDescription(64, BufferUsage.UniformBuffer | BufferUsage.Dynamic)); Matrix4x4 worldMatrix = Matrix4x4.CreateTranslation(0, 15000, -5000) * Matrix4x4.CreateRotationX(3 * (float)Math.PI / 2) * Matrix4x4.CreateScale(0.05f); GraphicsDevice.UpdateBuffer(_worldBuffer, 0, ref worldMatrix); ResourceLayout layout = factory.CreateResourceLayout(new ResourceLayoutDescription( new ResourceLayoutElementDescription("Projection", ResourceKind.UniformBuffer, ShaderStages.Vertex), new ResourceLayoutElementDescription("View", ResourceKind.UniformBuffer, ShaderStages.Vertex), new ResourceLayoutElementDescription("World", ResourceKind.UniformBuffer, ShaderStages.Vertex), new ResourceLayoutElementDescription("Bones", ResourceKind.UniformBuffer, ShaderStages.Vertex), new ResourceLayoutElementDescription("SurfaceTex", ResourceKind.TextureReadOnly, ShaderStages.Fragment), new ResourceLayoutElementDescription("SurfaceSampler", ResourceKind.Sampler, ShaderStages.Fragment))); Texture texture; using (Stream ktxStream = OpenEmbeddedAssetStream("goblin_bc3_unorm.ktx")) { texture = KtxFile.LoadTexture( GraphicsDevice, factory, ktxStream, PixelFormat.BC3_UNorm); } _texView = ResourceFactory.CreateTextureView(texture); VertexLayoutDescription vertexLayouts = new VertexLayoutDescription( new[] { new VertexElementDescription("Position", VertexElementSemantic.TextureCoordinate, VertexElementFormat.Float3), new VertexElementDescription("UV", VertexElementSemantic.TextureCoordinate, VertexElementFormat.Float2), new VertexElementDescription("BoneWeights", VertexElementSemantic.TextureCoordinate, VertexElementFormat.Float4), new VertexElementDescription("BoneIndices", VertexElementSemantic.TextureCoordinate, VertexElementFormat.UInt4), }); GraphicsPipelineDescription gpd = new GraphicsPipelineDescription( BlendStateDescription.SingleOverrideBlend, DepthStencilStateDescription.DepthOnlyLessEqual, new RasterizerStateDescription(FaceCullMode.Back, PolygonFillMode.Solid, FrontFace.CounterClockwise, true, false), PrimitiveTopology.TriangleList, new ShaderSetDescription( new[] { vertexLayouts }, factory.CreateFromSpirv( new ShaderDescription(ShaderStages.Vertex, Encoding.UTF8.GetBytes(VertexCode), "main"), new ShaderDescription(ShaderStages.Fragment, Encoding.UTF8.GetBytes(FragmentCode), "main"))), layout, GraphicsDevice.SwapchainFramebuffer.OutputDescription); _pipeline = factory.CreateGraphicsPipeline(ref gpd); AssimpContext ac = new AssimpContext(); using (Stream modelStream = OpenEmbeddedAssetStream("goblin.dae")) { _scene = ac.ImportFileFromStream(modelStream, "dae"); } _rootNodeInverseTransform = _scene.RootNode.Transform; _rootNodeInverseTransform.Inverse(); _firstMesh = _scene.Meshes[0]; AnimatedVertex[] vertices = new AnimatedVertex[_firstMesh.VertexCount]; for (int i = 0; i < vertices.Length; i++) { vertices[i].Position = new Vector3(_firstMesh.Vertices[i].X, _firstMesh.Vertices[i].Y, _firstMesh.Vertices[i].Z); vertices[i].UV = new Vector2(_firstMesh.TextureCoordinateChannels[0][i].X, _firstMesh.TextureCoordinateChannels[0][i].Y); } _animation = _scene.Animations[0]; List <int> indices = new List <int>(); foreach (Face face in _firstMesh.Faces) { if (face.IndexCount == 3) { indices.Add(face.Indices[0]); indices.Add(face.Indices[1]); indices.Add(face.Indices[2]); } } for (uint boneID = 0; boneID < _firstMesh.BoneCount; boneID++) { Bone bone = _firstMesh.Bones[(int)boneID]; _boneIDsByName.Add(bone.Name, boneID); foreach (VertexWeight weight in bone.VertexWeights) { vertices[weight.VertexID].AddBone(boneID, weight.Weight); } } Array.Resize(ref _boneTransformations, _firstMesh.BoneCount); _bonesBuffer = ResourceFactory.CreateBuffer(new BufferDescription( 64 * 64, BufferUsage.UniformBuffer | BufferUsage.Dynamic)); _rs = factory.CreateResourceSet(new ResourceSetDescription(layout, _projectionBuffer, _viewBuffer, _worldBuffer, _bonesBuffer, _texView, GraphicsDevice.Aniso4xSampler)); _indexCount = (uint)indices.Count; _vertexBuffer = ResourceFactory.CreateBuffer(new BufferDescription( (uint)(vertices.Length * Unsafe.SizeOf <AnimatedVertex>()), BufferUsage.VertexBuffer)); GraphicsDevice.UpdateBuffer(_vertexBuffer, 0, vertices); _indexBuffer = ResourceFactory.CreateBuffer(new BufferDescription( _indexCount * 4, BufferUsage.IndexBuffer)); GraphicsDevice.UpdateBuffer(_indexBuffer, 0, indices.ToArray()); _cl = factory.CreateCommandList(); _camera.Position = new Vector3(110, -87, -532); _camera.Yaw = 0.45f; _camera.Pitch = -0.55f; _camera.MoveSpeed = 1000f; _camera.FarDistance = 100000; }
protected override void CreateResources(ResourceFactory factory) { _instanceCount = 8000u; _camera.Position = new Vector3(-36f, 20f, 100f); _camera.Pitch = -0.3f; _camera.Yaw = 0.1f; _cameraProjViewBuffer = factory.CreateBuffer( new BufferDescription((uint)(Unsafe.SizeOf <Matrix4x4>() * 2), BufferUsage.UniformBuffer | BufferUsage.Dynamic)); _lightInfoBuffer = factory.CreateBuffer(new BufferDescription(32, BufferUsage.UniformBuffer | BufferUsage.Dynamic)); _rotationInfoBuffer = factory.CreateBuffer(new BufferDescription(16, BufferUsage.UniformBuffer | BufferUsage.Dynamic)); _lightDir = Vector3.Normalize(new Vector3(0.3f, -0.75f, -0.3f)); VertexLayoutDescription sharedVertexLayout = new VertexLayoutDescription( new VertexElementDescription("Position", VertexElementSemantic.Position, VertexElementFormat.Float3), new VertexElementDescription("Normal", VertexElementSemantic.Normal, VertexElementFormat.Float3), new VertexElementDescription("TexCoord", VertexElementSemantic.TextureCoordinate, VertexElementFormat.Float2)); bool etc2Supported = GraphicsDevice.GetPixelFormatSupport( PixelFormat.ETC2_R8_G8_B8_UNorm, TextureType.Texture2D, TextureUsage.Sampled); PixelFormat pixelFormat = etc2Supported ? PixelFormat.ETC2_R8_G8_B8_UNorm : PixelFormat.BC3_UNorm; byte[] rockTextureData = LoadEmbeddedAsset <byte[]>( etc2Supported ? "texturearray_rocks_etc2_unorm.binary" : "texturearray_rocks_bc3_unorm.binary"); Texture rockTexture = KtxFile.LoadTexture( GraphicsDevice, ResourceFactory, rockTextureData, pixelFormat); TextureView rockTextureView = ResourceFactory.CreateTextureView(rockTexture); ResourceLayoutElementDescription[] resourceLayoutElementDescriptions = { new ResourceLayoutElementDescription("ProjView", ResourceKind.UniformBuffer, ShaderStages.Vertex), new ResourceLayoutElementDescription("RotationInfo", ResourceKind.UniformBuffer, ShaderStages.Vertex), new ResourceLayoutElementDescription("LightInfo", ResourceKind.UniformBuffer, ShaderStages.Fragment), }; ResourceLayoutDescription resourceLayoutDescription = new ResourceLayoutDescription(resourceLayoutElementDescriptions); ResourceLayout sharedLayout = factory.CreateResourceLayout(resourceLayoutDescription); ResourceLayoutElementDescription[] textureLayoutDescriptions = { new ResourceLayoutElementDescription("Tex", ResourceKind.TextureReadOnly, ShaderStages.Fragment), new ResourceLayoutElementDescription("Samp", ResourceKind.Sampler, ShaderStages.Fragment) }; ResourceLayout textureLayout = factory.CreateResourceLayout(new ResourceLayoutDescription(textureLayoutDescriptions)); BindableResource[] bindableResources = new BindableResource[] { _cameraProjViewBuffer, _rotationInfoBuffer, _lightInfoBuffer }; ResourceSetDescription resourceSetDescription = new ResourceSetDescription(sharedLayout, bindableResources); _sharedResourceSet = factory.CreateResourceSet(resourceSetDescription); BindableResource[] instanceBindableResources = { rockTextureView, GraphicsDevice.LinearSampler }; _instanceTextureSet = factory.CreateResourceSet(new ResourceSetDescription(textureLayout, instanceBindableResources)); ProcessedModel rock = LoadEmbeddedAsset <ProcessedModel>("rock01.binary"); _rockModel = rock.MeshParts[0].CreateDeviceResources(GraphicsDevice, ResourceFactory); VertexLayoutDescription vertexLayoutPerInstance = new VertexLayoutDescription( new VertexElementDescription("InstancePosition", VertexElementSemantic.Position, VertexElementFormat.Float3), new VertexElementDescription("InstanceRotation", VertexElementSemantic.TextureCoordinate, VertexElementFormat.Float3), new VertexElementDescription("InstanceScale", VertexElementSemantic.TextureCoordinate, VertexElementFormat.Float3), new VertexElementDescription("InstanceTexArrayIndex", VertexElementSemantic.TextureCoordinate, VertexElementFormat.Int1)); vertexLayoutPerInstance.InstanceStepRate = 1; _instanceVB = ResourceFactory.CreateBuffer(new BufferDescription(InstanceInfo.Size * _instanceCount, BufferUsage.VertexBuffer)); InstanceInfo[] infos = new InstanceInfo[_instanceCount]; Random r = new Random(); float orbitDistance = 50f; for (uint i = 0; i < _instanceCount / 2; i++) { float angle = (float)(r.NextDouble() * Math.PI * 2); infos[i] = new InstanceInfo( new Vector3( ((float)Math.Cos(angle) * orbitDistance) + (float)(-10 + r.NextDouble() * 20), (float)(-1.5 + r.NextDouble() * 3), ((float)Math.Sin(angle) * orbitDistance) + (float)(-10 + r.NextDouble() * 20)), new Vector3( (float)(r.NextDouble() * Math.PI * 2), (float)(r.NextDouble() * Math.PI * 2), (float)(r.NextDouble() * Math.PI * 2)), new Vector3((float)(0.65 + r.NextDouble() * 0.35)), r.Next(0, (int)rockTexture.ArrayLayers)); } orbitDistance = 100f; for (uint i = _instanceCount / 2; i < _instanceCount; i++) { float angle = (float)(r.NextDouble() * Math.PI * 2); infos[i] = new InstanceInfo( new Vector3( ((float)Math.Cos(angle) * orbitDistance) + (float)(-10 + r.NextDouble() * 20), (float)(-1.5 + r.NextDouble() * 3), ((float)Math.Sin(angle) * orbitDistance) + (float)(-10 + r.NextDouble() * 20)), new Vector3( (float)(r.NextDouble() * Math.PI * 2), (float)(r.NextDouble() * Math.PI * 2), (float)(r.NextDouble() * Math.PI * 2)), new Vector3((float)(0.65 + r.NextDouble() * 0.35)), r.Next(0, (int)rockTexture.ArrayLayers)); } GraphicsDevice.UpdateBuffer(_instanceVB, 0, infos); Shader instanceVS = LoadShader(ResourceFactory, "Instance", ShaderStages.Vertex, "VS"); Shader instanceFS = LoadShader(ResourceFactory, "Instance", ShaderStages.Fragment, "FS"); GraphicsPipelineDescription pipelineDescription = new GraphicsPipelineDescription() { BlendState = BlendStateDescription.SingleOverrideBlend, DepthStencilState = new DepthStencilStateDescription( depthTestEnabled: true, depthWriteEnabled: true, comparisonKind: ComparisonKind.LessEqual), RasterizerState = new RasterizerStateDescription( cullMode: FaceCullMode.Back, fillMode: PolygonFillMode.Solid, frontFace: FrontFace.Clockwise, depthClipEnabled: true, scissorTestEnabled: false ), PrimitiveTopology = PrimitiveTopology.TriangleList, ResourceLayouts = new ResourceLayout[] { sharedLayout, textureLayout }, ShaderSet = new ShaderSetDescription( // The ordering of layouts directly impacts shader layout schemes vertexLayouts: new VertexLayoutDescription[] { sharedVertexLayout, vertexLayoutPerInstance }, shaders: new Shader[] { instanceVS, instanceFS } ), Outputs = MainSwapchain.Framebuffer.OutputDescription }; _instancePipeline = factory.CreateGraphicsPipeline(pipelineDescription); // Create planet Pipeline // Almost everything is the same as the rock Pipeline, // except no instance vertex buffer is needed, and different shaders are used. pipelineDescription.ShaderSet = new ShaderSetDescription( new[] { sharedVertexLayout }, new[] { LoadShader(ResourceFactory, "Planet", ShaderStages.Vertex, "VS"), LoadShader(ResourceFactory, "Planet", ShaderStages.Fragment, "FS"), }); _planetPipeline = ResourceFactory.CreateGraphicsPipeline(pipelineDescription); ProcessedModel planet = LoadEmbeddedAsset <ProcessedModel>("sphere.binary"); _planetModel = planet.MeshParts[0].CreateDeviceResources(GraphicsDevice, ResourceFactory); byte[] planetTexData = LoadEmbeddedAsset <byte[]>( etc2Supported ? "lavaplanet_etc2_unorm.binary" : "lavaplanet_bc3_unorm.binary"); Texture planetTexture = KtxFile.LoadTexture(GraphicsDevice, ResourceFactory, planetTexData, pixelFormat); TextureView planetTextureView = ResourceFactory.CreateTextureView(planetTexture); _planetTextureSet = ResourceFactory.CreateResourceSet(new ResourceSetDescription(textureLayout, planetTextureView, GraphicsDevice.Aniso4xSampler)); // Starfield resources ResourceLayout invCameraInfoLayout = ResourceFactory.CreateResourceLayout(new ResourceLayoutDescription( new ResourceLayoutElementDescription("InvCameraInfo", ResourceKind.UniformBuffer, ShaderStages.Fragment))); _viewInfoBuffer = ResourceFactory.CreateBuffer(new BufferDescription((uint)Unsafe.SizeOf <InvCameraInfo>(), BufferUsage.UniformBuffer | BufferUsage.Dynamic)); _viewInfoSet = ResourceFactory.CreateResourceSet(new ResourceSetDescription(invCameraInfoLayout, _viewInfoBuffer)); ShaderSetDescription starfieldShaders = new ShaderSetDescription( Array.Empty <VertexLayoutDescription>(), new[] { LoadShader(ResourceFactory, "Starfield", ShaderStages.Vertex, "VS"), LoadShader(ResourceFactory, "Starfield", ShaderStages.Fragment, "FS"), }); _starfieldPipeline = ResourceFactory.CreateGraphicsPipeline(new GraphicsPipelineDescription( BlendStateDescription.SingleOverrideBlend, DepthStencilStateDescription.Disabled, RasterizerStateDescription.CullNone, PrimitiveTopology.TriangleList, starfieldShaders, new[] { invCameraInfoLayout }, MainSwapchain.Framebuffer.OutputDescription)); _commandList = factory.CreateCommandList(); }
protected override void CreateResources(ResourceFactory factory) { _instanceCount = 1 << 14; _camera.Position = new Vector3(-36f, 20f, 100f); _camera.Pitch = -0.3f; _camera.Yaw = 0.1f; _cameraProjViewBuffer = factory.CreateBuffer( new BufferDescription((uint)(Unsafe.SizeOf <Matrix4x4>() * 2), BufferUsage.UniformBuffer | BufferUsage.Dynamic)); _lightInfoBuffer = factory.CreateBuffer(new BufferDescription(32, BufferUsage.UniformBuffer | BufferUsage.Dynamic)); _rotationInfoBuffer = factory.CreateBuffer(new BufferDescription(16, BufferUsage.UniformBuffer | BufferUsage.Dynamic)); _lightDir = Vector3.Normalize(new Vector3(0.3f, -0.75f, -0.3f)); VertexLayoutDescription sharedVertexLayout = new VertexLayoutDescription( new VertexElementDescription("Position", VertexElementSemantic.TextureCoordinate, VertexElementFormat.Float3), new VertexElementDescription("Normal", VertexElementSemantic.TextureCoordinate, VertexElementFormat.Float3), new VertexElementDescription("TexCoord", VertexElementSemantic.TextureCoordinate, VertexElementFormat.Float2)); bool etc2Supported = GraphicsDevice.GetPixelFormatSupport( PixelFormat.ETC2_R8_G8_B8_UNorm, TextureType.Texture2D, TextureUsage.Sampled); PixelFormat pixelFormat = etc2Supported ? PixelFormat.ETC2_R8_G8_B8_UNorm : PixelFormat.BC3_UNorm; byte[] rockTextureData = LoadEmbeddedAsset <byte[]>( etc2Supported ? "texturearray_rocks_etc2_unorm.binary" : "texturearray_rocks_bc3_unorm.binary"); Texture rockTexture = KtxFile.LoadTexture( GraphicsDevice, ResourceFactory, rockTextureData, pixelFormat); TextureView rockTextureView = ResourceFactory.CreateTextureView(rockTexture); ResourceLayoutElementDescription[] resourceLayoutElementDescriptions = { new ResourceLayoutElementDescription("LightInfo", ResourceKind.UniformBuffer, ShaderStages.Fragment), new ResourceLayoutElementDescription("ProjView", ResourceKind.UniformBuffer, ShaderStages.Vertex), new ResourceLayoutElementDescription("RotationInfo", ResourceKind.UniformBuffer, ShaderStages.Vertex), }; ResourceLayoutDescription resourceLayoutDescription = new ResourceLayoutDescription(resourceLayoutElementDescriptions); ResourceLayout sharedLayout = factory.CreateResourceLayout(resourceLayoutDescription); ResourceLayoutElementDescription[] textureLayoutDescriptions = { new ResourceLayoutElementDescription("Tex", ResourceKind.TextureReadOnly, ShaderStages.Fragment), new ResourceLayoutElementDescription("Samp", ResourceKind.Sampler, ShaderStages.Fragment) }; ResourceLayout textureLayout = factory.CreateResourceLayout(new ResourceLayoutDescription(textureLayoutDescriptions)); BindableResource[] bindableResources = new BindableResource[] { _lightInfoBuffer, _cameraProjViewBuffer, _rotationInfoBuffer }; ResourceSetDescription resourceSetDescription = new ResourceSetDescription(sharedLayout, bindableResources); _sharedResourceSet = factory.CreateResourceSet(resourceSetDescription); BindableResource[] instanceBindableResources = { rockTextureView, GraphicsDevice.LinearSampler }; _instanceTextureSet = factory.CreateResourceSet(new ResourceSetDescription(textureLayout, instanceBindableResources)); ProcessedModel rock = LoadEmbeddedAsset <ProcessedModel>("sphere.binary"); _rockModel = rock.MeshParts[0].CreateDeviceResources(GraphicsDevice, ResourceFactory); VertexLayoutDescription vertexLayoutPerInstancePos = new VertexLayoutDescription( new VertexElementDescription("InstancePosition", VertexElementSemantic.TextureCoordinate, VertexElementFormat.Float3)); VertexLayoutDescription vertexLayoutPerInstanceScale = new VertexLayoutDescription( new VertexElementDescription("InstanceScale", VertexElementSemantic.TextureCoordinate, VertexElementFormat.Float3)); VertexLayoutDescription vertexLayoutPerInstanceColor = new VertexLayoutDescription( new VertexElementDescription("InstanceColor", VertexElementSemantic.TextureCoordinate, VertexElementFormat.Float3)); vertexLayoutPerInstancePos.InstanceStepRate = 1; vertexLayoutPerInstanceScale.InstanceStepRate = 1; vertexLayoutPerInstanceColor.InstanceStepRate = 1; _instanceVBPos = ResourceFactory.CreateBuffer(new BufferDescription(Vector3.Size * _instanceCount, BufferUsage.VertexBuffer)); _instanceVBScale = ResourceFactory.CreateBuffer(new BufferDescription(Vector3.Size * _instanceCount, BufferUsage.VertexBuffer)); _instanceVBColor = ResourceFactory.CreateBuffer(new BufferDescription(Vector3.Size * _instanceCount, BufferUsage.VertexBuffer)); Vector3[] instancePos = new Vector3[_instanceCount]; Vector3[] instanceScale = new Vector3[_instanceCount]; Vector3[] instanceColors = new Vector3[_instanceCount]; Random r = new Random(); for (uint i = 0; i < _instanceCount; i++) { instancePos[i] = new Vector3( 1000.0f * ((float)r.NextDouble() - 0.5f), 200.0f * ((float)r.NextDouble() - 0.5f), 1000.0f * ((float)r.NextDouble() - 0.5f)); instanceScale[i] = new Vector3((float)(r.NextDouble() * 0.3)); instanceColors[i] = new Vector3((float)r.NextDouble() * 0.5f + 0.5f, (float)r.NextDouble() * 0.5f + 0.5f, (float)r.NextDouble() * 0.5f + 0.5f); } GraphicsDevice.UpdateBuffer(_instanceVBPos, 0, instancePos); GraphicsDevice.UpdateBuffer(_instanceVBScale, 0, instanceScale); GraphicsDevice.UpdateBuffer(_instanceVBColor, 0, instanceColors); GraphicsPipelineDescription pipelineDescriptionRocks = new GraphicsPipelineDescription() { BlendState = BlendStateDescription.SingleOverrideBlend, DepthStencilState = new DepthStencilStateDescription( depthTestEnabled: true, depthWriteEnabled: true, comparisonKind: ComparisonKind.LessEqual), RasterizerState = new RasterizerStateDescription( cullMode: FaceCullMode.Back, fillMode: PolygonFillMode.Solid, frontFace: FrontFace.Clockwise, depthClipEnabled: true, scissorTestEnabled: false ), PrimitiveTopology = PrimitiveTopology.TriangleList, ResourceLayouts = new ResourceLayout[] { sharedLayout, textureLayout }, ShaderSet = new ShaderSetDescription( // The ordering of layouts directly impacts shader layout schemes vertexLayouts: new VertexLayoutDescription[] { sharedVertexLayout, vertexLayoutPerInstancePos, vertexLayoutPerInstanceScale, vertexLayoutPerInstanceColor }, shaders: LoadShaders("Instance") ), Outputs = MainSwapchain.Framebuffer.OutputDescription }; _instancePipeline = factory.CreateGraphicsPipeline(pipelineDescriptionRocks); // Starfield resources ResourceLayout invCameraInfoLayout = ResourceFactory.CreateResourceLayout(new ResourceLayoutDescription( new ResourceLayoutElementDescription("InvCameraInfo", ResourceKind.UniformBuffer, ShaderStages.Fragment))); _viewInfoBuffer = ResourceFactory.CreateBuffer(new BufferDescription((uint)Unsafe.SizeOf <MatrixPair>(), BufferUsage.UniformBuffer | BufferUsage.Dynamic)); _commandList = factory.CreateCommandList(); }
public void Dispose() { tex2D = null; }
/// <summary> /// Encodes all cubemap faces and mipmap levels into a Ktx file. /// Order is +X, -X, +Y, -Y, +Z, -Z. Back maps to positive Z and front to negative Z. /// </summary> public KtxFile EncodeCubeMapToKtx(Image <Rgba32> right, Image <Rgba32> left, Image <Rgba32> top, Image <Rgba32> down, Image <Rgba32> back, Image <Rgba32> front) { KtxFile output; IBcBlockEncoder compressedEncoder = null; IRawEncoder uncompressedEncoder = null; if (right.Width != left.Width || right.Width != top.Width || right.Width != down.Width || right.Width != back.Width || right.Width != front.Width || right.Height != left.Height || right.Height != top.Height || right.Height != down.Height || right.Height != back.Height || right.Height != front.Height) { throw new ArgumentException("All input images of a cubemap should be the same size."); } Image <Rgba32>[] faces = new[] { right, left, top, down, back, front }; if (OutputOptions.format.IsCompressedFormat()) { compressedEncoder = GetEncoder(OutputOptions.format); if (compressedEncoder == null) { throw new NotSupportedException($"This format is not supported: {OutputOptions.format}"); } output = new KtxFile( KtxHeader.InitializeCompressed(right.Width, right.Height, compressedEncoder.GetInternalFormat(), compressedEncoder.GetBaseInternalFormat())); } else { uncompressedEncoder = GetRawEncoder(OutputOptions.format); output = new KtxFile( KtxHeader.InitializeUncompressed(right.Width, right.Height, uncompressedEncoder.GetGlType(), uncompressedEncoder.GetGlFormat(), uncompressedEncoder.GetGlTypeSize(), uncompressedEncoder.GetInternalFormat(), uncompressedEncoder.GetBaseInternalFormat())); } uint numMipMaps = (uint)OutputOptions.maxMipMapLevel; if (!OutputOptions.generateMipMaps) { numMipMaps = 1; } uint mipLength = MipMapper.CalculateMipChainLength(right.Width, right.Height, numMipMaps); for (uint i = 0; i < mipLength; i++) { output.MipMaps.Add(new KtxMipmap(0, 0, 0, (uint)faces.Length)); } for (int f = 0; f < faces.Length; f++) { var mipChain = MipMapper.GenerateMipChain(faces[f], ref numMipMaps); for (int i = 0; i < numMipMaps; i++) { byte[] encoded = null; if (OutputOptions.format.IsCompressedFormat()) { var blocks = ImageToBlocks.ImageTo4X4(mipChain[i].Frames[0], out int blocksWidth, out int blocksHeight); encoded = compressedEncoder.Encode(blocks, blocksWidth, blocksHeight, OutputOptions.quality, !Debugger.IsAttached && Options.multiThreaded); } else { encoded = uncompressedEncoder.Encode(mipChain[i].GetPixelSpan()); } if (f == 0) { output.MipMaps[i] = new KtxMipmap((uint)encoded.Length, (uint)mipChain[i].Width, (uint)mipChain[i].Height, (uint)faces.Length); } output.MipMaps[i].Faces[f] = new KtxMipFace(encoded, (uint)mipChain[i].Width, (uint)mipChain[i].Height); } foreach (var image in mipChain) { image.Dispose(); } } output.Header.NumberOfFaces = (uint)faces.Length; output.Header.NumberOfMipmapLevels = mipLength; return(output); }
/** * Load a 2D texture including all mip levels * * @param filename File to load (supports .ktx and .dds) * @param format Vulkan format of the image data stored in the file * @param device Vulkan device to create the texture on * @param copyQueue Queue used for the texture staging copy commands (must support transfer) * @param (Optional) imageUsageFlags Usage flags for the texture's image (defaults to VK_IMAGE_USAGE_SAMPLED_BIT) * @param (Optional) imageLayout Usage layout for the texture (defaults VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) * @param (Optional) forceLinear Force linear tiling (not advised, defaults to false) * */ public void FromTextureAsset( TextureAsset asset, GraphicsDevice device, VkImageUsageFlags imageUsageFlags = VkImageUsageFlags.Sampled, VkImageLayout imageLayout = VkImageLayout.ShaderReadOnlyOptimal) { VkFormat format = VkFormat.Undefined; KtxFile tex2D = asset.GetTexture(); format = GlFormatToVulkanFormat.vkGetFormatFromOpenGLInternalFormat(tex2D.Header.GlInternalFormat, tex2D.Header.GlFormat, tex2D.Header.GlType); width = tex2D.Header.PixelWidth; height = tex2D.Header.PixelHeight; if (height == 0) { height = width; } mipLevels = tex2D.Header.NumberOfMipmapLevels; this.imageLayout = imageLayout; this.format = format; this.device = device; // Get device properites for the requested texture format VkFormatProperties formatProperties; vkGetPhysicalDeviceFormatProperties(device.physicalDevice, format, out formatProperties); VkMemoryAllocateInfo memAllocInfo = Initializers.memoryAllocateInfo(); VkMemoryRequirements memReqs; // Create optimal tiled target image VkImageCreateInfo imageCreateInfo = Initializers.imageCreateInfo(); imageCreateInfo.imageType = VkImageType.Image2D; imageCreateInfo.format = format; imageCreateInfo.mipLevels = mipLevels; imageCreateInfo.arrayLayers = 1; imageCreateInfo.samples = VkSampleCountFlags.Count1; imageCreateInfo.tiling = VkImageTiling.Optimal; imageCreateInfo.sharingMode = VkSharingMode.Exclusive; imageCreateInfo.initialLayout = VkImageLayout.Undefined; imageCreateInfo.extent = new VkExtent3D { width = width, height = height, depth = 1 }; imageCreateInfo.usage = imageUsageFlags; // Ensure that the TRANSFER_DST bit is set for staging if ((imageCreateInfo.usage & VkImageUsageFlags.TransferDst) == 0) { imageCreateInfo.usage |= VkImageUsageFlags.TransferDst; } Util.CheckResult(vkCreateImage(device.device, &imageCreateInfo, null, out image)); vkGetImageMemoryRequirements(device.device, image, &memReqs); memAllocInfo.allocationSize = memReqs.size; memory = device.memoryAllocator.Allocate(memReqs.size, memReqs.alignment, memReqs.memoryTypeBits, false); Util.CheckResult(vkBindImageMemory(device.device, image, memory.vkDeviceMemory, memory.offset)); //memAllocInfo.memoryTypeIndex = device.vulkanDevice.getMemoryType(memReqs.memoryTypeBits, VkMemoryPropertyFlags.DeviceLocal); //Util.CheckResult(vkAllocateMemory(device.device, &memAllocInfo, null, out deviceMemory)); //Util.CheckResult(vkBindImageMemory(device.device, image, deviceMemory, 0)); TransferDataKtx(tex2D); CreateSampler(); CreateView(); // Update descriptor image info member that can be used for setting up descriptor sets UpdateDescriptor(); }