# 3D 生成

> 全部文档的索引见 [llms.txt](https://ark.volcengine.com/llms.txt)；在任一文档页面地址后追加 `.md` 可获取该页的 Markdown 版本。

3D 生成模型具备出色的三维内容生成能力，能够根据用户输入的图像，快速生成具备多边形面片与 PBR 材质的高精度 3D 资产。通过这篇教程您可以学习到如何调用豆包 3D 生成模型 API 来生成 3D 文件。

> **说明**
>
> 方舟平台的新用户？获取 API Key 及 开通模型等准备工作，请参见 [快速入门](/docs/quick-start)。

<span id="8dfef42f"></span>
# 模型及API

|**模型 ID（Model ID）** |**模型能力** |**产物规格** | **限流** |API |
|---|---|---|---|---|
|[doubao-seed3d-2-0-260328](https://ark.volcengine.com/region:cn-beijing/model/detail?Id=doubao-seed3d-2-0) |图生3D |**产物面数** |最大 RPM：300 |[3D生成 API](https://ark.volcengine.com/region:cn-beijing/docs/seed3d-api) | \
| | | |最大并发：5 | | \
| |* 生成带纹理和 PBR 材质的3D文件 |* 100000 面 | | | \
| | |* 500000 面 | | | \
| | |* 1000000面**产物格式** | | | \
| | |glb 、obj、usd、usdz | | |
|[hyper3d-gen2-260112](https://ark.volcengine.com/region:cn-beijing/model/detail?Id=hyper3d-gen2) |文生3D、图生3D |**产物面数** |最大 RPM: 60 |[影眸 API](https://ark.volcengine.com/region:cn-beijing/docs/shadoweye-api) | \
| | | |最大并发: 3 | | \
| |* 生成带纹理和 PBR 材质的 3D 文件 |* 三角面模型：[500, 1,000,000] | | | \
| | |* 四边面模型：[1,000, 200,000]**产物格式** | | | \
| | | | | | \
| | |* glb, obj, stl, fbx, usdz | | |
|[hitem3d-2-0-251223](https://ark.volcengine.com/region:cn-beijing/model/detail?Id=hitem3d-2-0) |图生3D |**产物面数** |最大 RPM: 600 |[数美 API](https://ark.volcengine.com/region:cn-beijing/docs/shumei-api) | \
| | | |最大并发: 30 | | \
| |* 标准白膜 |* [100000, 2000000]**产物格式** | | | \
| |* 标准纹理模型 | | | | \
| |* 高精白膜 |* glb, obj, stl, fbx, usdz**产物分辨率** | | | \
| |* 高精纹理模型 | | | | \
| | |* 1536、1536 pro | | |

<span id="022cff4d"></span>
# 模型价格

请参见：[3D生成模型](/docs/model-pricing#59e650ae)
<span id="2c6a9e64"></span>
# 使用流程

3D 生成任务为异步任务。
1. 通过 **创建任务接口** 创建3D生成任务，获得 **任务 ID** 。
2. 通过 **任务查询接口** + **任务 ID** 查询任务状态，直到任务完成时，获取返回的 3D 产物下载链接，下载生成结果。

> **说明**
>
> * 3D 生成过程耗时在分钟级别，通常在数分钟到十数分钟不等，具体受任务复杂度和平台负载影响。
> * 返回的产物链接有效期为 24 小时，过时失效，请及时下载/转存产物。

<span id="678a02b3"></span>
# 体验&调试

可在 [API Explorer](https://api.volcengine.com/api-explorer/?action=Create3DGenerationsTasks&groupName=3D%E7%94%9F%E6%88%90API&serviceCode=ark&tab=2&tab_result=1&tab_sdk=CURL&version=2024-01-01)，0代码体验通过 API 调用 3D 模型。支持灵活调整参数（如控制输出产物格式等），方便您直观感受模型实际使用过程。
<span id="dae0ab53"></span>
# 代码调用

<span id="9d67f944"></span>
## 效果预览

|输入 |输出预览 | |
|---|---|---|
|![](https://p9-arcosite.byteimg.com/tos-cn-i-goo7wpa0wc/078de189655e40c989543d829d421e2d~tplv-goo7wpa0wc-image.image) |![](https://p9-arcosite.byteimg.com/tos-cn-i-goo7wpa0wc/2e27978997d04e52a51ccf27beb6c25e~tplv-goo7wpa0wc-image.image) | | \
| | | | \
| |> 上图为效果展示。实际产物为3D文件，类似 [文件](https://arkdocs.tos-cn-beijing.volces.com/files/domain-models/seed3d_imageTo3d.glb)。 | |

<span id="2d432731"></span>
## 示例代码

**选项：Python**

```Python
import os
import time
# Install SDK:  python -m pip install --upgrade arkruntime
from arkruntime import Ark

# 初始化Ark客户端
client = Ark(
    # The base URL for model invocation .
    base_url="https://ark.cn-beijing.volces.com/api/v3",
    # Get API Key: https://ark.volcengine.com/region:cn-beijing/apikey
    api_key=os.getenv('ARK_API_KEY'),
)

print("----- create request -----")
# 创建3D生成任务
create_result = client.content_generation.tasks.create(
    # Replace with Model ID .
    model="doubao-seed3d-2-0-260328",
    content=[
        {
            # 参数
            "type": "text",
            "text": "--subdivisionlevel medium --fileformat glb"
        },
        {
            # 图片URL
            "type": "image_url",
            "image_url": {
                "url": "https://arkdocs.tos-cn-beijing.volces.com/images/domain-models/seed3d_imageTo3d.png"
            }
        }
    ]
)
print(create_result)

# 轮询查询部分
print("----- polling task status -----")
task_id = create_result.id
max_attempts = 60
for attempt in range(1, max_attempts + 1):
    get_result = client.content_generation.tasks.get(task_id=task_id)
    status = get_result.status
    if status == "succeeded":
        print("----- task succeeded -----")
        print(get_result)
        break
    elif status == "failed":
        print("----- task failed -----")
        print(f"Error: {get_result.error}")
        break
    if attempt == max_attempts:
        raise TimeoutError(f"Task {task_id} did not finish after {max_attempts} attempts")
    print(f"Current status: {status}, retrying after 60 seconds...")
    time.sleep(60)
```

**选项：Curl**

1. 创建3D生成任务，获取 3D 生成任务 ID。

```Bash
curl -X POST https://ark.cn-beijing.volces.com/api/v3/contents/generations/tasks \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $ARK_API_KEY" \
  -d '{
    "model": "doubao-seed3d-2-0-260328",
    "content": [
        {
            "type": "text",
            "text": "--subdivisionlevel medium --fileformat glb"
        },
        {
            "type": "image_url",
            "image_url": {
                "url": "https://arkdocs.tos-cn-beijing.volces.com/images/domain-models/seed3d_imageTo3d.png"
            }
        }
    ]
}'
```

2. 查询 3D 生成任务状态，直至任务状态为`Success`，返回产物下载链接。

> 替换  <TASK_ID>  为你的任务 ID。

```Bash
curl -X GET https://ark.cn-beijing.volces.com/api/v3/contents/generations/tasks/<TASK_ID> \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $ARK_API_KEY"
```

**选项：Go**

```Go
package main

import (
    "context"
    "fmt"
    "os"
    "time"
    "github.com/volcengine/ark-runtime-go/arkruntime"
    model "github.com/volcengine/ark-runtime-go/arkruntime/model/contentgeneration"
)

func main() {
    // 请确保您已将 API Key 存储在环境变量 ARK_API_KEY 中
    // 初始化Ark客户端，从环境变量中读取您的API Key
    client := arkruntime.NewClientWithApiKey(
            // 从环境变量中获取您的 API Key。此为默认方式，您可根据需要进行修改
            os.Getenv("ARK_API_KEY"),
            // The base URL for model invocation .
            arkruntime.WithBaseUrl("https://ark.cn-beijing.volces.com/api/v3"),
    )
    ctx := context.Background()

    // Replace with Model ID .
    modelEp := "doubao-seed3d-2-0-260328"

    // 创建任务
    fmt.Println("----- create request -----")
    createReq := &model.CreateContentGenerationTaskRequest{
        Model: modelEp,
        Content: []model.ContentItem{
            {
                Type: model.ContentTypeText,
                Text: model.NewOptString("--subdivisionlevel medium --fileformat glb"),
            },
            {
                Type: model.ContentTypeImageURL,
                ImageURL: model.NewOptImageURL(model.ImageURL{
                        URL: "https://arkdocs.tos-cn-beijing.volces.com/images/domain-models/seed3d_imageTo3d.png",
                        }),
                },
        },
    }
    createResp, err := client.CreateContentGenerationTask(ctx, createReq)
    if err != nil {
        fmt.Printf("create content generation error: %v", err)
        return
    }
    taskID := createResp.ID
    fmt.Printf("Task Created with ID: %s", taskID)

    // 轮询查询部分
    fmt.Println("----- polling task status -----")
    const maxAttempts = 60
    for attempt := 1; attempt <= maxAttempts; attempt++ {
        getReq := taskID
        getResp, err := client.GetContentGenerationTask(ctx, getReq)
        if err != nil {
            fmt.Printf("get content generation task error: %v", err)
            return
        }

        status := getResp.Status
        if status == "succeeded" {
            fmt.Println("----- task succeeded -----")
            fmt.Printf("Task ID: %s", getResp.ID)
            fmt.Printf("Model: %s", getResp.Model)
            fmt.Printf("File URL: %s", getResp.Content.Or(model.TaskContent{}).FileURL.Or(""))
            fmt.Printf("Completion Tokens: %d", getResp.Usage.Or(model.TaskUsage{}).CompletionTokens)
            fmt.Printf("Created At: %d, Updated At: %d", getResp.CreatedAt.Or(0), getResp.UpdatedAt.Or(0))
            return
        } else if status == "failed" {
            fmt.Println("----- task failed -----")
            if getResp.Error.IsSet() {
                fmt.Printf("Error Code: %s, Message: %s", getResp.Error.Value.Code, getResp.Error.Value.Message)
            }
            return
        } else if attempt == maxAttempts {
            fmt.Printf("Task %s did not finish after %d attempts", taskID, maxAttempts)
            return
        } else {
            fmt.Printf("Current status: %s, retrying in 60 seconds...", status)
            time.Sleep(60 * time.Second)
        }
    }
}
```

**选项：Java**

```Java
package com.volcengine.ark.runtime;

import com.volcengine.ark.runtime.models.content_generation.*;
import com.volcengine.ark.runtime.service.ArkService;
import okhttp3.ConnectionPool;
import okhttp3.Dispatcher;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;


public class ContentGenerationTaskExample {
    // 请确保您已将 API Key 存储在环境变量 ARK_API_KEY 中
    // 初始化Ark客户端，从环境变量中读取您的API Key
    static String apiKey = System.getenv("ARK_API_KEY");
    static ConnectionPool connectionPool = new ConnectionPool(5, 1, TimeUnit.SECONDS);
    static Dispatcher dispatcher = new Dispatcher();
    static ArkService service = ArkService.builder()
           .dispatcher(dispatcher)
           .connectionPool(connectionPool)
           .apiKey(apiKey)
           .baseUrl("https://ark.cn-beijing.volces.com/api/v3")
           // The base URL for model invocation .
           .build();
    public static void main(String[] args) {
        //Replace with Model ID .
        String model = "doubao-seed3d-2-0-260328";

        System.out.println("----- CREATE Task Request -----");
        List<ContentItem> contents = new ArrayList<>();

        // 参数
        contents.add(ContentItem.builder()
                .type(ContentType.TEXT)
                .text("--subdivisionlevel medium --fileformat glb")
                .build());

        // 图片URL
        contents.add(ContentItem.builder()
                .type(ContentType.IMAGE_URL)
                .imageUrl(ImageURL.builder()
                        .url("https://arkdocs.tos-cn-beijing.volces.com/images/domain-models/seed3d_imageTo3d.png")
                        .build())
                .build());

        // 创建3D生成任务
        CreateContentGenerationTaskRequest createRequest = CreateContentGenerationTaskRequest.builder()
                .model(model)
                .content(contents)
                .build();

        CreateContentGenerationTaskResponse createResult = service.createContentGenerationTask(createRequest);
        System.out.println(createResult);

        System.out.println("----- GET Task Request -----");

        // 获取任务详情
        String getRequest = createResult.getId();

        ContentGenerationTask getResult = service.getContentGenerationTask(getRequest);
        System.out.println(getResult);

        System.out.println("----- LIST Task Request -----");

        // 轮询查询部分
        System.out.println("----- polling task status -----");
        final int maxAttempts = 60;
        for (int attempt = 1; attempt <= maxAttempts; attempt++) {
            try {
                ContentGenerationTask getResponse = service.getContentGenerationTask(getRequest);
                String status = getResponse.getStatus().toString();
                if ("succeeded".equalsIgnoreCase(status)) {
                    System.out.println("----- task succeeded -----");
                    System.out.println(getResponse);
                    break;
                } else if ("failed".equalsIgnoreCase(status)) {
                    System.out.println("----- task failed -----");
                    System.out.println("Error: " + getResponse.getStatus());
                    break;
                } else if (attempt == maxAttempts) {
                    throw new IllegalStateException(
                            "Task " + getRequest + " did not finish after " + maxAttempts + " attempts");
                } else {
                    System.out.printf("Current status: %s, retrying in 60 seconds...", status);
                    TimeUnit.SECONDS.sleep(60);
                }
            } catch (InterruptedException ie) {
                Thread.currentThread().interrupt();
                System.err.println("Polling interrupted");
                break;
            }
        }
    }
}
```

<span id="fa4a40a8"></span>
# 其他说明

<span id="fd7485a1"></span>
## 控制产物参数

通过`--[parameters]`的方式，控制3D文件输出的规格，包括产物面的数量、输出文格式等。

```Bash
# 指定生成的3D文件的多边形面的数量为 100000 面，生成的3D文件格式为 glb
"content": [
        {
            "type": "text",
            "text": "--subdivisionlevel medium --fileformat glb"
        }
    ]
```

<span id="cd628783"></span>
## 链接有效期

任务数据（如任务状态、URL等）仅保留24小时，超时后会被自动清除。请您务必及时保存生成的文件。

<span id="cf80a262"></span>
## 限流说明

同一主账号下，使用同一模型（不区分版本）的请求受到以下限制。超过对应限制会返回 “429: 'Too Many Requests'” 错误。

### 模型维度的限流

> 不同模型的 RPM 和最大并发任务数不同，详见 [3D生成能力](/docs/model-list#21b9a17a)。

* **RPM（Requests Per Minute）**：每分钟允许创建的 3D 生成任务数上限。超过上限时，创建任务请求将因触发限流而失败。
* **最大并发任务数**：同一时刻可处于处理状态的任务数上限。达到上限后，新创建的任务将进入队列等待处理。

### 非推理 API 请求频率限制

* **QPS（Queries Per Second）**：每秒允许的请求数上限。超过上限时，任务请求将因触发限流而失败。

|接口名称 |账号维度的 QPS 上限 |
|---|---|
|查询 3D 生成任务 |20 |
|查询 3D 生成任务列表 |1 |
|取消或删除 3D 生成任务 |20 |
