【Metal】使用渲染管道渲染基元(III)

渲染一個(gè)簡(jiǎn)單的二維三角形

下載

概觀

在使用Metal繪制到屏幕時(shí),您學(xué)習(xí)了如何設(shè)置MTKView對(duì)象并使用渲染過(guò)程更改視圖的內(nèi)容只祠。該示例只是將視圖的內(nèi)容刪除為背景顏色。此示例顯示如何配置渲染管道并將其用作渲染過(guò)程的一部分,以將簡(jiǎn)單的2D彩色三角形繪制到視圖中挣轨。該示例為每個(gè)頂點(diǎn)提供位置和顏色,渲染管道使用該數(shù)據(jù)渲染三角形轩猩,在為三角形頂點(diǎn)指定的顏色之間插入顏色卷扮。

image

注意
Xcode項(xiàng)目包含在macOS荡澎,iOS和tvOS上運(yùn)行示例的方案。iOS或者tvOS模擬器不支持Metal晤锹,因此iOS和tvOS方案需要物理設(shè)備來(lái)運(yùn)行示例衔瓮。默認(rèn)方案是macOS。

了解Metal Render Pipeline

一個(gè)渲染管線流程繪圖命令和數(shù)據(jù)寫(xiě)入到一個(gè)渲染通道的目標(biāo)抖甘。渲染管道有許多階段热鞍,一些使用著著色器編程,另一些使用固定或可配置的行為編程衔彻。此示例主要關(guān)注管道的三個(gè)階段:頂點(diǎn)階段薇宠、光柵化階段和片段階段。頂點(diǎn)階段和片段階段是可編程的艰额,因此您可以使用MSL為他們編寫(xiě)函數(shù)澄港。光柵化階段具有固定的行為。

圖1 Metal圖形化渲染管道的主要階段

image

渲染從繪圖命令開(kāi)始柄沮,該命令包括頂點(diǎn)的計(jì)數(shù)和要渲染的基元類(lèi)型回梧。例如,以下是此示例中的繪圖命令:

renderEncoder?.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: 3)

頂點(diǎn)階段為每個(gè)頂點(diǎn)提供數(shù)據(jù)祖搓。當(dāng)處理了足夠的頂點(diǎn)時(shí)狱意,渲染管道柵格化基元,確定渲染目標(biāo)中的哪些像素基于基元的邊界內(nèi)拯欧。片段階段確定要寫(xiě)入這些元素的渲染目標(biāo)的值详囤。

在本示例的其余部分,您將看到如何編寫(xiě)頂點(diǎn)和片段函數(shù)镐作,如何創(chuàng)建渲染管道狀態(tài)對(duì)象藏姐,以及最后如何編碼使用此管道的繪制命令。

決定自定義渲染管道如何處理數(shù)據(jù)

頂點(diǎn)函數(shù)為單個(gè)頂點(diǎn)生成數(shù)據(jù)该贾,片段函數(shù)生成單個(gè)片段的數(shù)據(jù)羔杨,但您可以決定它們的工作方式。您可以考慮目標(biāo)來(lái)配置管道的各個(gè)階段杨蛋,這意味著您知道管道要生成什么以及如何生成這些結(jié)果兜材。

確定要傳遞到渲染管道的數(shù)據(jù)以及將哪些數(shù)據(jù)傳遞到管道的后續(xù)階段。通常有三個(gè)地方可以執(zhí)行此操作:

  • 管道的輸入六荒,由您的應(yīng)用程序提供并傳遞到頂點(diǎn)护姆。
  • 頂點(diǎn)階段的輸出,傳遞光柵化階段掏击。
  • 片段階段的輸入卵皂,由您的應(yīng)用提供或光柵化階段生成。

在此示例中砚亭,管道的輸入數(shù)據(jù)是頂點(diǎn)的位置及顏色灯变。為了演示通常在頂點(diǎn)函數(shù)中執(zhí)行的變換類(lèi)型殴玛,輸入坐標(biāo)在自定義坐標(biāo)空間中定義,以視圖中心的像素為單位進(jìn)行測(cè)量添祸。

聲明一個(gè)AAPLVertex結(jié)構(gòu)滚粟,使用SIMD矢量類(lèi)型來(lái)保存位置和顏色數(shù)據(jù)。要共享結(jié)構(gòu)在內(nèi)存中的布局方式的單個(gè)定義刃泌,請(qǐng)?jiān)谕ㄓ妙^文件中聲明結(jié)構(gòu)凡壤,并將其導(dǎo)入著色器(Metal shader)和app中。

typedef struct
{
    vector_float2 position;
    vector_float4 color;
} AAPLVertex;

SIMD類(lèi)型在MSL中很常見(jiàn)耙替,您也應(yīng)該在應(yīng)用程序中使用simd庫(kù)亚侠。SIMD類(lèi)型包含特定數(shù)據(jù)類(lèi)型的多個(gè)通道,因此將位置聲明為vector_float2包含兩個(gè)32位浮點(diǎn)數(shù)(他將保存x和y坐標(biāo))俗扇。顏色需要使用vector_float4硝烂,因?yàn)樗鼈冇?個(gè)通道R、G铜幽、B滞谢、A。

在應(yīng)用程序中除抛,使用常亮數(shù)組輸入指定數(shù)據(jù):

let triangleVertices: [AAPLVertex] =
            [AAPLVertex(position: [250, -250], color: [1, 0, 0, 1]),
             AAPLVertex(position: [-250, -250], color: [0, 1, 0, 1]),
             AAPLVertex(position: [0, 250], color: [0, 0, 1, 1])]

頂點(diǎn)階段為頂點(diǎn)生成數(shù)據(jù)狮杨,因此需要提供顏色和變換位置。使用SIMD類(lèi)型聲明RasterizerData包含位置和顏色值的結(jié)構(gòu)镶殷。

typedef struct
{
    float4 position [[position]];
    float4 color;
} RasterizerData;

輸出位置(下面詳細(xì)描述)必須定義為vector_float4禾酱,顏色聲明為輸入數(shù)據(jù)結(jié)構(gòu)的顏色。

您需要告訴Metal光柵化數(shù)據(jù)中哪個(gè)字段提供位置數(shù)據(jù)绘趋,因?yàn)?strong>Metal不對(duì)結(jié)構(gòu)中的字段強(qiáng)制執(zhí)行任何特定的命名約定。position使用[[position]]屬性限定符注釋該字段以聲明此字段包含輸出位置颗管。

片段函數(shù)只是將光柵化階段的數(shù)據(jù)傳遞給后期階段陷遮,因=因此它不需要任何其他參數(shù)。

聲明頂點(diǎn)函數(shù)

聲明頂點(diǎn)函數(shù)垦江,包括其輸入?yún)?shù)及輸出數(shù)據(jù)帽馋。就像使用kernel關(guān)鍵字聲明計(jì)算函數(shù)一樣,您使用vertex關(guān)鍵字聲明頂點(diǎn)函數(shù)比吭。

vertex RasterizerData
vertexShader(uint vertexID [[vertex_id]],
             constant AAPLVertex *vertices [[buffer(AAPLVertexInputIndexVertices)]],
             constant vector_uint2 *viewportSizePointer [[buffer(AAPLVertexInputIndexViewportSize)]])

第一個(gè)參數(shù)是vertexID绽族,使用[[vertex_id]]屬性限定符,他還是一個(gè)Metal關(guān)鍵字衩藤。執(zhí)行渲染命令時(shí)吧慢,GPU會(huì)多次調(diào)用頂點(diǎn)函數(shù),為每一個(gè)頂點(diǎn)生成唯一值赏表。

第二個(gè)參數(shù)vertices是一個(gè)包含訂單數(shù)據(jù)的數(shù)組检诗,使用AAPLVertex先前定義的結(jié)構(gòu)匈仗。

要將位置轉(zhuǎn)換為Metal的坐標(biāo),該函數(shù)需要繪制三角形的視口大蟹昊拧(以像素為單位)悠轩,因此將其存儲(chǔ)在viewportSizePointer參數(shù)中。

第二個(gè)和第三個(gè)參數(shù)具有[buffer(n)]屬性限定符攻泼,默認(rèn)情況下火架,Metal會(huì)自動(dòng)為參數(shù)表分配每個(gè)參數(shù)的插槽。將[[buffer(n)]]限定符添加到緩沖區(qū)參數(shù)時(shí)忙菠,可以明確告知Metal使用哪個(gè)插槽何鸡。明確聲明插槽可以更輕松地修改著色器,而無(wú)需更改應(yīng)用程序代碼只搁。在共享頭文件中聲明兩個(gè)指標(biāo)的常量音比。

函數(shù)的輸出是一個(gè)RasterizerData結(jié)構(gòu)。

寫(xiě)入頂點(diǎn)函數(shù)

您的頂點(diǎn)函數(shù)必須生成輸出結(jié)構(gòu)的兩個(gè)字段氢惋。使用vertexID參數(shù)索引到數(shù)組并讀取頂點(diǎn)的輸入數(shù)據(jù)洞翩。另外,檢索視口尺寸焰望。

float2 pixelSpacePosition = vertices[vertexID].position.xy;

vector_float2 viewportSize = vector_float2(*viewportSizePointer);

頂點(diǎn)函數(shù)必須在clip-space坐標(biāo)空間中提供位置數(shù)據(jù)骚亿,這是使用四維同質(zhì)矢量(x, y, z, w)指定的3D點(diǎn)。光柵化階段采用輸出位置并將x熊赖,y和z坐標(biāo)除以w来屠,以在標(biāo)準(zhǔn)化設(shè)備坐標(biāo)中生成3D點(diǎn)。標(biāo)準(zhǔn)化設(shè)備坐標(biāo)與視口大小無(wú)關(guān)震鹉。

標(biāo)準(zhǔn)化設(shè)備坐標(biāo)使用左手坐標(biāo)系并映射到視口中的位置俱笛。基元被剪切到此坐標(biāo)系中的框传趾,然后進(jìn)行柵格化迎膜。剪切框的左下角位于(x,y)坐標(biāo)的(-1浆兰,-1.0)磕仅,右上角位于(1.0, 1.0)。正z值指向遠(yuǎn)離相機(jī)(進(jìn)入屏幕)簸呈。坐標(biāo)的可見(jiàn)部分位于(近剪輯平面)和(遠(yuǎn)剪輯平面)之間榕订。

圖2 標(biāo)準(zhǔn)化設(shè)備坐標(biāo)系
[圖片上傳失敗...(image-f39984-1565142312808)]

您的頂點(diǎn)函數(shù)需要將輸入坐標(biāo)系轉(zhuǎn)化為此輸出坐標(biāo)系。

因?yàn)檫@是一個(gè)2D應(yīng)用程序而不需要同質(zhì)坐標(biāo)蜕便,所以首先默認(rèn)值寫(xiě)入輸出坐標(biāo)劫恒,其w值設(shè)置為1.0,其他坐標(biāo)設(shè)置為0.0玩裙。這意味著坐標(biāo)已在標(biāo)準(zhǔn)化設(shè)備坐標(biāo)空間中兼贸,并且頂點(diǎn)函數(shù)應(yīng)該在該坐標(biāo)空間中生成(x, y)坐標(biāo)段直。將輸入位置除以視口大小一半以生成標(biāo)準(zhǔn)化設(shè)備坐標(biāo)。由于使用SIMD類(lèi)型執(zhí)行該計(jì)算溶诞,因此可以使用單行代碼同時(shí)劃分兩個(gè)通道鸯檬。執(zhí)行除法并將結(jié)果放在輸出位置的xy通道中。

out.position = vector_float4(0.0, 0.0, 0.0, 1.0);
out.position.xy = pixelSpacePosition / (viewportSize / 2.0);

最后將顏色值復(fù)制到返回值的out.color中螺垢。

out.color = vertices[vertexID].color;

寫(xiě)一個(gè)片段函數(shù)

一個(gè)片段是一個(gè)可能改變的渲染目標(biāo)喧务。光柵化器確定渲染目標(biāo)的哪些像素被基元覆蓋。僅渲染像素中心在三角形內(nèi)部的片段枉圃。

圖3 光柵化階段生成的碎片

image

片段函數(shù)處理來(lái)自光柵化器的輸入信息功茴,用于單個(gè)位置,并計(jì)算每個(gè)渲染目標(biāo)的輸出值孽亲。這些片段由管道中的后續(xù)階段處理坎穿,最終寫(xiě)入渲染目標(biāo)。

注意
片段被稱為可能更改的原因是因?yàn)槠坞A段之后的管道階段可以配置為拒絕某些片段或更改寫(xiě)入渲染目標(biāo)的內(nèi)容。在此示例中,片段階段計(jì)算的所有值都按原樣寫(xiě)入渲染目標(biāo)摩瞎。

此示例中的片段著色器接收與頂點(diǎn)著色器輸出中聲明的相同參數(shù)。使用fragment關(guān)鍵字聲明片段函數(shù)孵延。他需要一個(gè)參數(shù),與頂點(diǎn)階段提供的結(jié)構(gòu)RasterizerData相同亲配。添加[[stage_in]]限定符以指示此參數(shù)是由光柵化器生成尘应。

fragment float4 fragmentShader(RasterizerData in [[stage_in]])

如果您的片段函數(shù)寫(xiě)入多個(gè)渲染目標(biāo),則它必須為每個(gè)渲染目標(biāo)聲明一個(gè)包含字段的結(jié)構(gòu)吼虎。由于此示例僅具有單個(gè)渲染目標(biāo)犬钢,因此您可以直接將浮點(diǎn)矢量指定為函數(shù)的輸出。此輸出的是要渲染目標(biāo)的顏色思灰。

光柵化階段計(jì)算每個(gè)片段參數(shù)的值娜饵,并使他們調(diào)用片段函數(shù)。光柵化階段將其顏色參數(shù)計(jì)算為三角形頂點(diǎn)處顏色的混合官辈。片段離頂點(diǎn)越近,頂點(diǎn)對(duì)最終顏色的貢獻(xiàn)越大遍坟。

圖4 插值片段顏色

image

將插值顏色作為函數(shù)的輸出返回

return in.color;

創(chuàng)建渲染管道狀態(tài)對(duì)象

現(xiàn)在函數(shù)已經(jīng)完成拳亿,您可以創(chuàng)建使用他們的渲染管道。首先愿伴,獲取默認(rèn)庫(kù)并獲取每個(gè)函數(shù)的MTLFunction對(duì)象肺魁。

let defaultLibrary = device?.makeDefaultLibrary()
        
let vertexFunction = defaultLibrary?.makeFunction(name: "vertexShader")
let framentFunction = defaultLibrary?.makeFunction(name: "fragmentShader")

接下里,創(chuàng)建一個(gè)MTLRenderPipelineState對(duì)象隔节,渲染管道有更多要配置的階段鹅经,因此您使用MTLRenderPipelineDescriptor來(lái)配置管道寂呛。

let pipelineStateDescriptor = MTLRenderPipelineDescriptor()
pipelineStateDescriptor.label = "Simple Pipeline"
pipelineStateDescriptor.vertexFunction = vertexFunction
pipelineStateDescriptor.fragmentFunction = framentFunction
pipelineStateDescriptor.colorAttachments[0].pixelFormat = mtkView.colorPixelFormat
        
do {
     try pipelineState = device?.makeRenderPipelineState(descriptor: pipelineStateDescriptor)
} catch {
     assert(false, "Failed to created pipline state: \(error.localizedDescription)")
}

除了指定頂點(diǎn)和片段函數(shù)之外,還要聲明管道將繪制的所有渲染目標(biāo)的像素格式瘾晃。像素格式(MTLPixelFormat)定義像素?cái)?shù)據(jù)的存儲(chǔ)器布局贷痪。對(duì)于簡(jiǎn)單格式,此定義包括每個(gè)像素的字節(jié)數(shù)蹦误,存儲(chǔ)在像素種的數(shù)據(jù)通道以及這些通道的位布局劫拢。此示例只有一個(gè)渲染目標(biāo)并且由視圖提供,因此將視圖的像素格式復(fù)制到渲染管道描述符中强胰。渲染管道狀態(tài)必須使用與渲染過(guò)程指定的像素格式舱沧,因此他們始終相同。

當(dāng)Metal創(chuàng)建渲染管道狀態(tài)對(duì)象偶洋,管道配置為將片段函數(shù)的輸出轉(zhuǎn)換為渲染目標(biāo)的像素格式熟吏。如果要定位不同的像素格式,則需要?jiǎng)?chuàng)建不同的管道狀態(tài)對(duì)象玄窝。您可以在針對(duì)不同像素格式的多個(gè)管道中重復(fù)使用相同的著色器牵寺。

設(shè)置視口

現(xiàn)在您已擁有管道的渲染管道狀態(tài)對(duì)象,您將渲染三角形哆料。您可以使用渲染命令編碼器執(zhí)行此操作缸剪。首先,設(shè)置視口东亦,以便Metal知道要繪制渲染目標(biāo)的哪個(gè)部分杏节。

 renderEncoder?.setViewport(MTLViewport(originX: 0.0,
                                        originY: 0.0,
                                        width: Double(viewportSize.x),
                                        height: Double(viewportSize.y),
                                        znear: 0.0,
                                        zfar: 1.0))

設(shè)置渲染管道狀態(tài)

設(shè)置要使用的管道的渲染管道狀態(tài)。

renderEncoder?.setRenderPipelineState(pipelineState)

將參數(shù)數(shù)據(jù)發(fā)送到頂點(diǎn)函數(shù)

通常典阵,您使用buffersMTLBuffer)將數(shù)據(jù)傳遞給著色器奋渔。但是,當(dāng)您需要將少量數(shù)據(jù)傳遞給頂點(diǎn)函數(shù)時(shí)(如此處所示)壮啊,將數(shù)據(jù)直接復(fù)制到命令緩沖區(qū)中嫉鲸。

該示例將兩個(gè)參數(shù)的數(shù)據(jù)復(fù)制到命令緩沖區(qū)中。頂點(diǎn)數(shù)據(jù)從樣本中定義的數(shù)組中復(fù)制歹啼。視口數(shù)據(jù)是從用于設(shè)置視口的相同變量中復(fù)制玄渗。

在此示例中,片段函數(shù)僅使用光柵化器接受的數(shù)據(jù)狸眼,因此沒(méi)有要設(shè)置的參數(shù)藤树。

renderEncoder?.setVertexBytes(triangleVertices,
                              length: MemoryLayout<AAPLVertex>.size * triangleVertices.count,
                              index: Int(AAPLVertexInputIndexVertices.rawValue))
        
renderEncoder?.setVertexBytes(&viewportSize,
                              length: MemoryLayout<vector_uint2>.size,
                              index: Int(AAPLVertexInputIndexViewportSize.rawValue))

編碼繪圖命令

指定基元的類(lèi)型,起始索引和頂點(diǎn)數(shù)拓萌。渲染三角形時(shí)岁钓,將vertexID參數(shù)值為0、1和2來(lái)調(diào)用頂點(diǎn)函數(shù)。

使用顏色插值進(jìn)行實(shí)驗(yàn)

在此示例中屡限,顏色值在三角形中進(jìn)行插值品嚣。這通常是你想要的,但有時(shí)你想要一個(gè)頂點(diǎn)生成一個(gè)值钧大,并在整個(gè)基元上保持不變翰撑。在頂點(diǎn)函數(shù)的輸出上指定flat屬性限定符以執(zhí)行此操作。現(xiàn)在試試吧拓型。在示例項(xiàng)目中找到RasterizerData的定義额嘿,并將[[flat]]限定符添加到其顏色字段中。

float4 color [[flat]];

再次運(yùn)行該示例劣挫。 渲染管道在三角形上均勻地使用第一個(gè)頂點(diǎn)(稱為激發(fā)頂點(diǎn))的顏色值册养,并忽略其他兩個(gè)頂點(diǎn)的顏色。 您可以使用平面著色和插值的混合压固,只需在頂點(diǎn)函數(shù)的輸出上添加或省略平坦限定符即可球拦。 “金屬著色語(yǔ)言”規(guī)范定義了您還可以用來(lái)修改光柵化行為的其他屬性限定符。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末帐我,一起剝皮案震驚了整個(gè)濱河市坎炼,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌拦键,老刑警劉巖谣光,帶你破解...
    沈念sama閱讀 222,681評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異芬为,居然都是意外死亡萄金,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)媚朦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)氧敢,“玉大人,你說(shuō)我怎么就攤上這事询张∷锕裕” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,421評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵份氧,是天一觀的道長(zhǎng)唯袄。 經(jīng)常有香客問(wèn)我,道長(zhǎng)蜗帜,這世上最難降的妖魔是什么越妈? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,114評(píng)論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮钮糖,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己店归,他們只是感情好阎抒,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,116評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著消痛,像睡著了一般且叁。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上秩伞,一...
    開(kāi)封第一講書(shū)人閱讀 52,713評(píng)論 1 312
  • 那天逞带,我揣著相機(jī)與錄音,去河邊找鬼纱新。 笑死展氓,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的脸爱。 我是一名探鬼主播遇汞,決...
    沈念sama閱讀 41,170評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼簿废!你這毒婦竟也來(lái)了空入?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 40,116評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤族檬,失蹤者是張志新(化名)和其女友劉穎歪赢,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體单料,經(jīng)...
    沈念sama閱讀 46,651評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡埋凯,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,714評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了看尼。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片递鹉。...
    茶點(diǎn)故事閱讀 40,865評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖藏斩,靈堂內(nèi)的尸體忽然破棺而出躏结,到底是詐尸還是另有隱情,我是刑警寧澤狰域,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布媳拴,位于F島的核電站,受9級(jí)特大地震影響兆览,放射性物質(zhì)發(fā)生泄漏屈溉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,211評(píng)論 3 336
  • 文/蒙蒙 一抬探、第九天 我趴在偏房一處隱蔽的房頂上張望子巾。 院中可真熱鬧帆赢,春花似錦、人聲如沸线梗。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,699評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)仪搔。三九已至瘾婿,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間烤咧,已是汗流浹背偏陪。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,814評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留煮嫌,地道東北人笛谦。 一個(gè)月前我還...
    沈念sama閱讀 49,299評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像立膛,于是被迫代替她去往敵國(guó)和親揪罕。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,870評(píng)論 2 361