diff --git a/src/main.cpp b/src/main.cpp
index 65aaf43..7b5d695 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -13,6 +13,7 @@
 #include <tiny_obj_loader.h>
 
 #include <chrono>
+#include <random>
 #include <iostream>
 #include <fstream>
 #include <vector>
@@ -38,6 +39,7 @@ private:
     /////////////////////////////////////////////////////////////////
     /// forwoard declare
     struct Vertex;
+    struct InstanceData;
     /////////////////////////////////////////////////////////////////
 
     /////////////////////////////////////////////////////////////////
@@ -47,6 +49,7 @@ private:
 
     inline static const std::string MODEL_PATH = "models/viking_room.obj";
     inline static const std::string TEXTURE_PATH = "textures/viking_room.png";
+    inline static const std::string BUNNY_PATH = "models/bunny.obj";
 
     inline static const std::vector<const char*> validationLayers {
         "VK_LAYER_KHRONOS_validation"
@@ -62,6 +65,7 @@ private:
     #endif
 
     static constexpr int MAX_FRAMES_IN_FLIGHT = 2;
+    static constexpr int BUNNY_NUMBER = 5;
     /////////////////////////////////////////////////////////////////
 
     /////////////////////////////////////////////////////////////////
@@ -76,9 +80,13 @@ private:
     vk::raii::Queue m_graphicsQueue{ nullptr };
     vk::raii::Queue m_presentQueue{ nullptr };
     std::vector<Vertex> m_vertices;
+    std::vector<InstanceData> m_instanceDatas;
     std::vector<uint32_t> m_indices;
+    std::vector<uint32_t> m_firstIndices;
     vk::raii::DeviceMemory m_vertexBufferMemory{ nullptr };
     vk::raii::Buffer m_vertexBuffer{ nullptr };
+    vk::raii::DeviceMemory m_instanceBufferMemory{ nullptr };
+    vk::raii::Buffer m_instanceBuffer{ nullptr };
     vk::raii::DeviceMemory m_indexBufferMemory{ nullptr };
     vk::raii::Buffer m_indexBuffer{ nullptr };
     std::vector<vk::raii::DeviceMemory> m_uniformBuffersMemory;
@@ -153,8 +161,11 @@ private:
         createTextureImage();
         createTextureImageView();
         createTextureSampler();
-        loadModel();
+        loadModel(MODEL_PATH);
+        loadModel(BUNNY_PATH);
+        initInstanceDatas();
         createVertexBuffer();
+        createInstanceBuffer();
         createIndexBuffer();
         createUniformBuffers();
         createDescriptorPool();
@@ -640,10 +651,28 @@ private:
 
         vk::PipelineVertexInputStateCreateInfo vertexInputInfo;
 
-        auto bindingDescription = Vertex::getBindingDescription();
-        auto attributeDescriptions = Vertex::getAttributeDescriptions();
+        auto vertexBindingDescription = Vertex::getBindingDescription();
+        auto vertexAttributeDescriptions = Vertex::getAttributeDescriptions();
+        auto instanceBindingDescription = InstanceData::getBindingDescription();
+        auto instanceAttributeDescriptions = InstanceData::getAttributeDescriptions();
 
-        vertexInputInfo.setVertexBindingDescriptions(bindingDescription);
+        std::vector<vk::VertexInputBindingDescription> bindingDescriptions = { 
+            vertexBindingDescription, 
+            instanceBindingDescription 
+        };
+        std::vector<vk::VertexInputAttributeDescription> attributeDescriptions;
+        attributeDescriptions.insert(
+            attributeDescriptions.end(),
+            vertexAttributeDescriptions.begin(),
+            vertexAttributeDescriptions.end()
+        );
+        attributeDescriptions.insert(
+            attributeDescriptions.end(),
+            instanceAttributeDescriptions.begin(),
+            instanceAttributeDescriptions.end()
+        );
+
+        vertexInputInfo.setVertexBindingDescriptions(bindingDescriptions);
         vertexInputInfo.setVertexAttributeDescriptions(attributeDescriptions);
 
         vk::PipelineInputAssemblyStateCreateInfo inputAssembly;
@@ -792,9 +821,9 @@ private:
         );
         commandBuffer.setScissor(0, scissor);
 
-        // std::array<vk::Buffer,1> vertexBuffers { m_vertexBuffer };
-        // std::array<vk::DeviceSize,1> offsets { 0 };
-        commandBuffer.bindVertexBuffers( 0, *m_vertexBuffer, vk::DeviceSize{0} );
+        vk::Buffer vertexBuffers[] = { *m_vertexBuffer, *m_instanceBuffer };
+        vk::DeviceSize offsets[] = { 0, 0 };
+        commandBuffer.bindVertexBuffers( 0, vertexBuffers, offsets );
         commandBuffer.bindIndexBuffer( m_indexBuffer, 0, vk::IndexType::eUint32 );
 
         commandBuffer.bindDescriptorSets(
@@ -805,7 +834,20 @@ private:
             nullptr
         );
 
-        commandBuffer.drawIndexed(static_cast<uint32_t>(m_indices.size()), 1, 0, 0, 0);
+        commandBuffer.drawIndexed( // draw the room
+            m_firstIndices[1],
+            1,
+            0,
+            0,
+            0
+        );
+        commandBuffer.drawIndexed( // draw the bunny
+            static_cast<uint32_t>(m_indices.size() - m_firstIndices[1]),
+            BUNNY_NUMBER,
+            m_firstIndices[1],
+            0, 
+            1
+        );
 
         commandBuffer.endRenderPass();
         commandBuffer.end();
@@ -969,7 +1011,8 @@ private:
         vk::BufferUsageFlags usage, 
         vk::MemoryPropertyFlags properties, 
         vk::raii::Buffer& buffer, 
-        vk::raii::DeviceMemory& bufferMemory) {
+        vk::raii::DeviceMemory& bufferMemory
+    ) {
 
         vk::BufferCreateInfo bufferInfo;
         bufferInfo.size = size;
@@ -1069,7 +1112,6 @@ private:
     /////////////////////////////////////////////////////////////////
     /// descriptor layout and buffer
     struct UniformBufferObject {
-        glm::mat4 model;
         glm::mat4 view;
         glm::mat4 proj;
     };
@@ -1124,16 +1166,6 @@ private:
         front = glm::normalize(front);
 
         UniformBufferObject ubo{};
-        ubo.model = glm::rotate(
-            glm::mat4(1.0f), 
-            glm::radians(-90.0f), 
-            glm::vec3(1.0f, 0.0f, 0.0f)
-        );
-        ubo.model *= glm::rotate(
-            glm::mat4(1.0f), 
-            glm::radians(-90.0f), 
-            glm::vec3(0.0f, 0.0f, 1.0f)
-        );
         ubo.view = glm::lookAt(
             m_cameraPos, 
             m_cameraPos + front, 
@@ -1573,17 +1605,19 @@ private:
 
     /////////////////////////////////////////////////////////////////
     /// load model
-    void loadModel() {
+    void loadModel(const std::string& model_path) {
         tinyobj::attrib_t attrib;
         std::vector<tinyobj::shape_t> shapes;
         std::vector<tinyobj::material_t> materials;
         std::string warn, err;
 
-        if (!tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, MODEL_PATH.c_str())) {
+        if (!tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, model_path.c_str())) {
             throw std::runtime_error(warn + err);
         }
+
+        m_firstIndices.push_back(m_indices.size());
         
-        std::unordered_map<
+        static std::unordered_map<
             Vertex, 
             uint32_t,
             decltype( [](const Vertex& vertex) -> size_t {
@@ -1605,10 +1639,14 @@ private:
                     attrib.vertices[3 * index.vertex_index + 2]
                 };
 
-                vertex.texCoord = {
-                    attrib.texcoords[2 * index.texcoord_index + 0],
-                    1.0f - attrib.texcoords[2 * index.texcoord_index + 1]
-                };
+                if (!attrib.texcoords.empty() && index.texcoord_index >= 0) {
+                    vertex.texCoord = {
+                        attrib.texcoords[2 * index.texcoord_index],
+                        1.0f - attrib.texcoords[2 * index.texcoord_index + 1]
+                    };
+                } else {
+                    vertex.texCoord = {0.0f, 0.0f};
+                }
 
                 vertex.color = {1.0f, 1.0f, 1.0f};
 
@@ -1821,6 +1859,97 @@ private:
         );
     }
     /////////////////////////////////////////////////////////////////
+
+    /////////////////////////////////////////////////////////////////
+    /// instance data
+    struct alignas(16) InstanceData {
+        glm::mat4 model;
+        uint32_t enableTexture;
+
+        static vk::VertexInputBindingDescription getBindingDescription() {
+            vk::VertexInputBindingDescription bindingDescription;
+            bindingDescription.binding = 1; // binding 1 for instance data
+            bindingDescription.stride = sizeof(InstanceData);
+            bindingDescription.inputRate = vk::VertexInputRate::eInstance;
+
+            return bindingDescription;
+        }
+
+        static std::array<vk::VertexInputAttributeDescription, 5>  getAttributeDescriptions() {
+            std::array<vk::VertexInputAttributeDescription, 5> attributeDescriptions;
+            for(uint32_t i = 0; i < 4; ++i) {
+                attributeDescriptions[i].binding = 1; // binding 1 for instance data
+                attributeDescriptions[i].location = 3 + i; // location 3, 4, 5, 6
+                attributeDescriptions[i].format = vk::Format::eR32G32B32A32Sfloat;
+                attributeDescriptions[i].offset = sizeof(glm::vec4) * i;
+            }
+            attributeDescriptions[4].binding = 1;
+            attributeDescriptions[4].location = 7;
+            attributeDescriptions[4].format = vk::Format::eR32Uint;
+            attributeDescriptions[4].offset = sizeof(glm::mat4);
+            return attributeDescriptions;
+        }
+    };
+    void initInstanceDatas() {
+        InstanceData instanceData;
+        m_instanceDatas.reserve(BUNNY_NUMBER + 1);
+        // room model
+        instanceData.model = glm::rotate(
+            glm::mat4(1.0f), 
+            glm::radians(-90.0f), 
+            glm::vec3(1.0f, 0.0f, 0.0f)
+        ) *  glm::rotate(
+            glm::mat4(1.0f), 
+            glm::radians(-90.0f), 
+            glm::vec3(0.0f, 0.0f, 1.0f)
+        );
+        instanceData.enableTexture = 1; // enable texture sampling
+        m_instanceDatas.emplace_back( instanceData );
+
+        std::random_device rd;
+        std::mt19937 gen(rd());
+        std::uniform_real_distribution<float> dis(-1.0f, 1.0f);
+        // initialize BUNNY_NUMBER instances
+        for (int i = 0; i < BUNNY_NUMBER; ++i) {
+            // randomly generate position and rotation
+            instanceData.model = glm::translate(
+                glm::mat4(1.0f), 
+                glm::vec3(dis(gen), dis(gen), dis(gen))
+            ) * glm::rotate(
+                glm::mat4(1.0f), 
+                glm::radians(dis(gen) * 180.0f), 
+                glm::vec3(0.0f, 1.0f, 0.0f)
+            );
+            instanceData.enableTexture = 0; // disable texture sampling
+            m_instanceDatas.emplace_back( instanceData );
+        }
+    }
+    void createInstanceBuffer() {
+        vk::DeviceSize bufferSize = sizeof(InstanceData) * m_instanceDatas.size();
+
+        vk::raii::DeviceMemory stagingBufferMemory{ nullptr };
+        vk::raii::Buffer stagingBuffer{ nullptr };
+        createBuffer(bufferSize,
+            vk::BufferUsageFlagBits::eTransferSrc,
+            vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent,
+            stagingBuffer,
+            stagingBufferMemory
+        );
+
+        void* data = stagingBufferMemory.mapMemory(0, bufferSize);
+        memcpy(data, m_instanceDatas.data(), static_cast<size_t>(bufferSize));
+        stagingBufferMemory.unmapMemory();
+
+        createBuffer(bufferSize,
+            vk::BufferUsageFlagBits::eTransferDst | vk::BufferUsageFlagBits::eVertexBuffer,
+            vk::MemoryPropertyFlagBits::eDeviceLocal,
+            m_instanceBuffer,
+            m_instanceBufferMemory
+        );
+
+        copyBuffer(stagingBuffer, m_instanceBuffer, bufferSize);
+    }
+    /////////////////////////////////////////////////////////////////
 };
 
 
